Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Faster alternatives to Java's reflection [closed]

As we know, reflection is a flexible but slow method to maintain and modify the behaviour of the code at runtime.

But if we have to use such a functionality, are there any faster programming techniques in Java compared to Reflection API for dynamic modifications? What are pros and cons of these alternatives against reflection?

like image 287
ovunccetin Avatar asked Oct 24 '13 05:10

ovunccetin


People also ask

Why is Java reflection slow?

Reflection is slow for a few obvious reasons: The compiler can do no optimization whatsoever as it can have no real idea about what you are doing. This probably goes for the JIT as well. Everything being invoked/created has to be discovered (i.e. classes looked up by name, methods looked at for matches etc)

Which method is used to get methods using reflection?

The getConstructors() method is used to get the public constructors of the class to which an object belongs. The getMethods() method is used to get the public methods of the class to which an object belongs.

What is Java Lang reflect?

java. lang. reflect package provides classes and interfaces which are used to obtain reflective information about classes and objects. Reflection API allows programmatic access to information about the fields, methods and constructors of loaded classes.


1 Answers

One alternative to Reflection is to generate a class file dynamically. This generated class ought to perform the desired action, e.g. invokes the method discovered at runtime, and implements an interface known at compile-time so that it’s possible to invoke the generated method in a non-reflective way using that interface. There’s one catch: if applicable, Reflection does the same trick internally. This does not work in special cases, e.g. when invoking a private method as you can’t generate a legal class file invoking it. So in the Reflection implementation there are different types of invocation handlers, using either generated code or native code. You can’t beat that.

But more important is that Reflection does security checks on every invocation. So your generated class will be checked on loading and instantiation only which can be a big win. Alternatively you can invoke setAccessible(true) on a Method instance to turn the security checks off. Then only the minor performance loss of autoboxing and varargs array creation remains.

Since Java 7 there is an alternative to both, the MethodHandle. The big advantage is that, unlike the other two, it even works in security restricted environments. The access checks for a MethodHandle are performed when acquiring it but not when invoking it. It has the so-called “polymorphic signature” which means you can invoke it with arbitrary argument types without auto-boxing nor array creation. Of course, wrong argument types will create an appropriate RuntimeException.

(Update) With Java 8, there is the option to use the back-end of the lambda expression and method reference language feature at runtime. This backend does exactly the thing described at the beginning, generating a class dynamically which implements an interface your code may call directly when it is known at compile-time. The exact mechanics is implementation-specific, hence undefined, but you can assume that the implementation will try it’s best to make the invocation as fast as possible. The current implementation of Oracle’s JRE does it perfectly. Not only that this saves you from the burden of generating such an accessor class, it is also capable of doing what you never could do— invoke even private methods via generated code. I have updated the example to include this solution. This example uses a standard interface which already exists and happens to have the desired method signature. If no such matching interface exists, you have to create your own accessor functional interface with a method with the right signature. But, of course, now the example code requires Java 8 to run.

Here is a simple benchmark example:

import java.lang.invoke.LambdaMetafactory; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.reflect.Method; import java.util.function.IntBinaryOperator;  public class TestMethodPerf {   private static final int ITERATIONS = 50_000_000;   private static final int WARM_UP = 10;    public static void main(String... args) throws Throwable   {  // hold result to prevent too much optimizations     final int[] dummy=new int[4];      Method reflected=TestMethodPerf.class       .getDeclaredMethod("myMethod", int.class, int.class);     final MethodHandles.Lookup lookup = MethodHandles.lookup();     MethodHandle mh=lookup.unreflect(reflected);     IntBinaryOperator lambda=(IntBinaryOperator)LambdaMetafactory.metafactory(       lookup, "applyAsInt", MethodType.methodType(IntBinaryOperator.class),       mh.type(), mh, mh.type()).getTarget().invokeExact();      for(int i=0; i<WARM_UP; i++)     {       dummy[0]+=testDirect(dummy[0]);       dummy[1]+=testLambda(dummy[1], lambda);       dummy[2]+=testMH(dummy[1], mh);       dummy[3]+=testReflection(dummy[2], reflected);     }     long t0=System.nanoTime();     dummy[0]+=testDirect(dummy[0]);     long t1=System.nanoTime();     dummy[1]+=testLambda(dummy[1], lambda);     long t2=System.nanoTime();     dummy[2]+=testMH(dummy[1], mh);     long t3=System.nanoTime();     dummy[3]+=testReflection(dummy[2], reflected);     long t4=System.nanoTime();     System.out.printf("direct: %.2fs, lambda: %.2fs, mh: %.2fs, reflection: %.2fs%n",       (t1-t0)*1e-9, (t2-t1)*1e-9, (t3-t2)*1e-9, (t4-t3)*1e-9);      // do something with the results     if(dummy[0]!=dummy[1] || dummy[0]!=dummy[2] || dummy[0]!=dummy[3])       throw new AssertionError();   }    private static int testMH(int v, MethodHandle mh) throws Throwable   {     for(int i=0; i<ITERATIONS; i++)       v+=(int)mh.invokeExact(1000, v);     return v;   }    private static int testReflection(int v, Method mh) throws Throwable   {     for(int i=0; i<ITERATIONS; i++)       v+=(int)mh.invoke(null, 1000, v);     return v;   }    private static int testDirect(int v)   {     for(int i=0; i<ITERATIONS; i++)       v+=myMethod(1000, v);     return v;   }    private static int testLambda(int v, IntBinaryOperator accessor)   {     for(int i=0; i<ITERATIONS; i++)       v+=accessor.applyAsInt(1000, v);     return v;   }    private static int myMethod(int a, int b)   {     return a<b? a: b;   } } 

Th old program printed in my Java 7 setup: direct: 0,03s, mh: 0,32s, reflection: 1,05s which suggested that MethodHandle was a good alternative. Now, the updated program running under Java 8 on the same machine printed direct: 0,02s, lambda: 0,02s, mh: 0,35s, reflection: 0,40s which clearly shows that Reflection performance has been improved to a degree that might make dealing with MethodHandle unnecessary, unless you use it to do the lambda trick, that clearly outperforms all reflective alternatives, which comes at no surprise, as it is just a direct call (well, almost: one level of indirection). Note that I made the target method private to demonstrate the capability of calling even private methods efficiently.

As always, I have to point at the simplicity of this benchmark and how artificial it is. But I think, the tendency is clearly visible and even more important, the results are convincingly explainable.

like image 153
Holger Avatar answered Sep 24 '22 15:09

Holger