有什么方法可以进一步优化Javareflection方法调用?

我想知道是否有任何额外的优化我可以实现以提高Java中reflection调用的速度。 并不是说性能是令人望而却步的,但是我在考虑库中的某些代码时会得到一些我正在编写的代码在某个地方实现紧密循环。

考虑一种reflection性调用的实用方法:

public static Object invoke(Object targetObject, String methodName, Object[] arguments, Class[] signature) 

基本操作是

 return method.invoke(targetObject, arguments); 

作为性能优化,我使用目标对象的类,方法名称和签名(其代码可能会使用某些改进)的哈希来缓存方法,但除此之外,还有什么我可以做的吗? 我听说过InvokeDynamic的一些早期实现的参考听起来很有希望,但我只是假设它们可能还不适用,并且我打算使用自己的字节代码操作,因为我希望保持实用程序简单(但速度快)。

干杯。

以下评论涉及Sun的实施,特别是OpenJDK 6.您的里程可能因其他Java平台实施而异。

java.lang.Class自己进行一些缓存,因此实​​现自己的缓存可能不会改进很多东西。 使用和不使用手动缓存进行计时测试。

实际的调用机制也得到了优化。 使用JNI调用reflection方法的前15次运行(默认情况下); 之后,生成字节码并调用reflection方法将直接在Java代码中调用该方法执行相同的操作。

我围绕Chris Jester-Young的答案进行了一些测试并使用了详细的选项,我确实观察到编译器在第15次调用时采取了一些措施。 如果没有更复杂的测试,很难说是否有很多性能差异,但它具有说服力。 这是输出:

 Test# 0 Test# 1 Test# 2 Test# 3 Test# 4 Test# 5 Test# 6 Test# 7 Test# 8 Test# 9 Test# 10 Test# 11 Test# 12 Test# 13 Test# 14 [Loaded sun.reflect.ClassFileConstants from C:\jdk1.5.0_06\jre\lib\rt.jar] [Loaded sun.reflect.AccessorGenerator from C:\jdk1.5.0_06\jre\lib\rt.jar] [Loaded sun.reflect.MethodAccessorGenerator from C:\jdk1.5.0_06\jre\lib\rt.jar] [Loaded sun.reflect.ByteVectorFactory from C:\jdk1.5.0_06\jre\lib\rt.jar] [Loaded sun.reflect.ByteVector from C:\jdk1.5.0_06\jre\lib\rt.jar] [Loaded sun.reflect.ByteVectorImpl from C:\jdk1.5.0_06\jre\lib\rt.jar] [Loaded sun.reflect.ClassFileAssembler from C:\jdk1.5.0_06\jre\lib\rt.jar] [Loaded sun.reflect.UTF8 from C:\jdk1.5.0_06\jre\lib\rt.jar] [Loaded java.lang.Void from C:\jdk1.5.0_06\jre\lib\rt.jar] [Loaded sun.reflect.Label from C:\jdk1.5.0_06\jre\lib\rt.jar] [Loaded sun.reflect.Label$PatchInfo from C:\jdk1.5.0_06\jre\lib\rt.jar] [Loaded java.util.AbstractList$Itr from C:\jdk1.5.0_06\jre\lib\rt.jar] [Loaded sun.reflect.MethodAccessorGenerator$1 from C:\jdk1.5.0_06\jre\lib\rt.jar] [Loaded sun.reflect.ClassDefiner from C:\jdk1.5.0_06\jre\lib\rt.jar] [Loaded sun.reflect.ClassDefiner$1 from C:\jdk1.5.0_06\jre\lib\rt.jar] [Loaded sun.reflect.GeneratedMethodAccessor1 from __JVM_DefineClass__] Test# 15 Test# 16 Test# 17 

我想InvokeDynamic业务并没有在reflection加速/消除的基础上吸引太多的开发人员。

谢谢克里斯。

您肯定希望仅将方法对象反映一次(可能作为私有静态),并将其用于调用,而不是每次都反映出来。 除非您在编译时不知道名称,否则不要打扰缓存映射。

如果它在您的上下文中是明智的,您可能希望继续并重用参数数组对象(不要忘记在方法退出时使数组元素为空以防止临时GC禁止)。

如果总是使用相同的parms(非常不可能)调用,你可以挂在parms上(带有它的值的参数数组)重用它们。

由于其他答案已经给出的原因,我不会尝试除此之外的任何事情。

过早优化通常很糟糕。 无论如何,你的表现仍然是动态语言的很多倍,所以除了记录它使用reflection这一事实并因此可能不是最佳之外,我真的不会担心它。

此外,现在或将来,Java很有可能优化字节码,以便在循环中使用时调用不会花费任何方法调用。 您的“优化”实际上可能会阻碍编译器执行此类操作的能力。 (我知道我很模糊,但这已经发生了 – 很多)。

如果调用成本低于方法成本的10%,那么几乎不值得担心。

您可以通过在循环中运行10 ^ 6次来确定它,无论是否有例程。 用秒表计时,所以秒转换为微秒。