有什么方法可以进一步优化Java反射方法的调用?

时间:2009-01-05 22:54:16

标签: java performance reflection

我想知道是否有任何额外的优化我可以实现以提高Java中反射调用的速度。并不是说性能太高,但是当我在编写库中的某些代码时,我得到了一些想法,我正在编写一个紧密循环的地方。

考虑一种反射性调用的实用工具方法:

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

基本操作是

return method.invoke(targetObject, arguments);

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

干杯。

5 个答案:

答案 0 :(得分:55)

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

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

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

答案 1 :(得分:8)

我围绕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 业务并没有在反映加速/消除的基础上吸引太多开发人员。

谢谢Chris。

答案 2 :(得分:1)

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

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

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

由于其他答案已经给出的理由,我不会尝试任何其他事情。

答案 3 :(得分:0)

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

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

答案 4 :(得分:0)

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

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