为什么Java lamdareflection性能会变差

我构建了一个简单的测试,用Java LambdaMetafactory测量类reflection的性能。 根据各种post,使用LambdaMetafactory的reflection与直接调用getter一样快。 这似乎是最初的,但过了一段时间性能下降。

其中一个测试节目(这似乎是大势所趋):

原来:

GET - REFLECTION: Runtime=2.841seconds GET - DIRECT: Runtime=0.358seconds GET - LAMBDA: Runtime=0.362seconds SET - REFLECTION: Runtime=3.86seconds SET - DIRECT: Runtime=0.507seconds SET - LAMBDA: Runtime=0.455seconds 

最后:

 GET - REFLECTION: Runtime=2.904seconds GET - DIRECT: Runtime=0.501seconds GET - LAMBDA: Runtime=5.299seconds SET - REFLECTION: Runtime=4.62seconds SET - DIRECT: Runtime=1.723seconds SET - LAMBDA: Runtime=5.149seconds 

代码如下。

问题:

  • 为什么?

  • 如何通过LambdaMetafactory提高性能?


 package lambda; public class Main { public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, Throwable { int runs = 5; for (int i = 0; i < runs; i++) { System.out.println("***** RUN " + i); Lambda lam = new Lambda(); lam.initGetter(Person.class, "getName"); lam.initSetter(Person.class, "setSalary", double.class); Test test = new Test(); test.doTest(lam); } } } 

 package lambda; import java.lang.invoke.CallSite; 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.BiConsumer; import java.util.function.Function; public final class Lambda { //https://www.optaplanner.org/blog/2018/01/09/JavaReflectionButMuchFaster.html //https://bytex.solutions/2017/07/java-lambdas/ //https://stackoverflow.com/questions/27602758/java-access-bean-methods-with-lambdametafactory private Function getterFunction; private BiConsumer setterFunction; private Function createGetter(final MethodHandles.Lookup lookup, final MethodHandle getter) throws Exception { final CallSite site = LambdaMetafactory.metafactory(lookup, "apply", MethodType.methodType(Function.class), MethodType.methodType(Object.class, Object.class), //signature of method Function.apply after type erasure getter, getter.type()); //actual signature of getter try { return (Function) site.getTarget().invokeExact(); } catch (final Exception e) { throw e; } catch (final Throwable e) { throw new Exception(e); } } private BiConsumer createSetter(final MethodHandles.Lookup lookup, final MethodHandle setter) throws Exception { final CallSite site = LambdaMetafactory.metafactory(lookup, "accept", MethodType.methodType(BiConsumer.class), MethodType.methodType(void.class, Object.class, Object.class), //signature of method BiConsumer.accept after type erasure setter, setter.type()); //actual signature of setter try { return (BiConsumer) site.getTarget().invokeExact(); } catch (final Exception e) { throw e; } catch (final Throwable e) { throw new Exception(e); } } public void initGetter(Class theSubject, String methodName) throws ReflectiveOperationException, Exception { MethodHandles.Lookup lookup = MethodHandles.lookup(); Method m = theSubject.getMethod(methodName); MethodHandle mh = lookup.unreflect(m); getterFunction = createGetter(lookup, mh); } public void initSetter(Class theSubject, String methodName, Class parameterType) throws ReflectiveOperationException, Exception { MethodHandles.Lookup lookup = MethodHandles.lookup(); Method m = theSubject.getMethod(methodName, parameterType); MethodHandle mh = lookup.unreflect(m); setterFunction = createSetter(lookup, mh); } public Object executeGetter(Object theObject) { return getterFunction.apply(theObject); } public void executeSetter(Object theObject, Object value) { setterFunction.accept(theObject, value); } } 

 package lambda; import java.lang.reflect.Field; public class Test { public void doTest(Lambda lam) throws NoSuchFieldException, IllegalArgumentException, Exception { if (lam == null) { lam = new Lambda(); lam.initGetter(Person.class, "getName"); lam.initSetter(Person.class, "setSalary", double.class); } Person p = new Person(); p.setName(111); long loops = 1000000000; // 10e9 System.out.println("Loops: " + loops); /// System.out.println("GET - REFLECTION:"); Field field = Person.class.getField("name"); long start = System.currentTimeMillis(); for (int i = 0; i < loops; i++) { int name = (int) field.get(p); } long end = System.currentTimeMillis(); System.out.println("Runtime=" + ((end - start) / 1000.0) + "seconds"); /// System.out.println("GET - DIRECT:"); start = System.currentTimeMillis(); for (int i = 0; i < loops; i++) { int name = (int) p.getName(); } end = System.currentTimeMillis(); System.out.println("Runtime=" + ((end - start) / 1000.0) + "seconds"); //// System.out.println("GET - LAMBDA:"); start = System.currentTimeMillis(); for (int i = 0; i < loops; i++) { int name = (int) lam.executeGetter(p); } end = System.currentTimeMillis(); System.out.println("Runtime=" + ((end - start) / 1000.0) + "seconds"); /// System.out.println("SET - REFLECTION:"); int name = 12; start = System.currentTimeMillis(); for (int i = 0; i < loops; i++) { field.set(p, name); } end = System.currentTimeMillis(); System.out.println("Runtime=" + ((end - start) / 1000.0) + "seconds"); /// System.out.println("SET - DIRECT:"); name = 33; start = System.currentTimeMillis(); for (int i = 0; i < loops; i++) { p.setName(name); } end = System.currentTimeMillis(); System.out.println("Runtime=" + ((end - start) / 1000.0) + "seconds"); //// System.out.println("SET - LAMBDA:"); Double name2 = 2.3; start = System.currentTimeMillis(); for (int i = 0; i < loops; i++) { lam.executeSetter(p, name2); } end = System.currentTimeMillis(); System.out.println("Runtime=" + ((end - start) / 1000.0) + "seconds"); } } 

 package lambda; public class Person { public int name; private double salary; public int getName() { return name; } public void setName(int name) { this.name = name; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } } 

正如后面的评论中所提到的,我已经更新了代码,以便作为一个独立的测试运行,我可以从我的应用程序中的任何一点调用它。 测试接受类,方法名称(字符串)和类实例以运行性能测试。 正如预期的那样,当我只使用一个类(Person1)时,性能很好。 一旦我为两个类调用另一个类(Person2)的性能下降测试 – 有时性能甚至比普通方法reflection调用更差。 下面是一些数据。 最初,Person1的表现还可以。 一旦使用了Person2,就会有一个下降。

 (Class=Person1, name=Name) Loops: 50000000 GET - DIRECT: Runtime=0.016seconds GET - LAMBDA: Runtime=0.016seconds (Class=Person2, name=Name) Loops: 50000000 GET - DIRECT: Runtime=0.019seconds createLambda: new getter for clazz=lambda.Person2 name=Name createLambda: new setter for clazz=lambda.Person2 name=Name GET - LAMBDA: Runtime=0.069seconds (Class=Person1, name=Name) Loops: 50000000 GET - DIRECT: Runtime=0.02seconds GET - LAMBDA: Runtime=0.045seconds (Class=Person2, name=Name) Loops: 50000000 GET - DIRECT: Runtime=0.017seconds GET - LAMBDA: Runtime=0.054seconds (Class=Person1, name=Name) Loops: 50000000 GET - DIRECT: Runtime=0.018seconds GET - LAMBDA: Runtime=0.045seconds