2009-01-05 93 views
18

我想知道是否有任何額外的優化,我可以實現,以提高Java反射調用的速度。並不是說性能過高,但是當我想到我正在編寫的庫中的某段代碼正在某個地方以緊密的循環實現時,我會得到意志。如何進一步優化Java反射方法調用?

考慮一個實用的方法來反思調用:

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

的基本操作是

return method.invoke(targetObject, arguments); 

作爲一個性能優化,我緩存使用目標對象的類,方法名的散列方法和簽名(其中的代碼可能會使用一些改進),但除此之外,還有什麼我可以做的嗎?我聽說過一些InvokeDynamic的早期實現,但我只是假設它們可能還不適用,並且我打折了自己的字節碼操作,因爲我希望保持實用程序簡單(但速度很快)。

乾杯。

回答

49

以下評論與Sun的實現有關,特別是OpenJDK 6.您的里程可能因其他Java平臺實現而異。

java.lang.Class做了一些緩存本身,所以實現自己的緩存可能不會改善很多東西。使用和不使用手動緩存進行計時測試。

實際的調用機制也進行了優化。反射方法的前15個運行(默認情況下)使用JNI調用;之後,會生成字節碼,並調用該反射方法直接在Java代碼中調用該方法。

+1

哇我從來不知道這一點。我會做這些時間測試。 有什麼方法可以覆蓋15個默認值? 任何其他方式說服編譯器提前踢? – Nicholas 2009-01-05 23:22:16

+8

只需將sun.reflect.inflationThreshold屬性設置爲您希望的數字即可。 – 2009-01-06 11:27:50

0

過早優化通常是不好的。無論如何,你的表現仍然是動態語言的許多倍,所以除了記錄它使用反射並因此可能不是最優的事實之外,我真的不會擔心它。

此外,還有一個很好的機會,無論是現在還是將來,Java將優化字節碼,這樣在循環中使用時調用不費什麼比一個方法調用更多。你的「優化」實際上可能會阻礙編譯器做這種事情的能力。 (我知道我很模糊,但是這發生了 - 很多)。

+0

已被提供,但在有點鬼鬼祟祟的過程中,我發現非公開的方法需要爬繼承樹才能在父母中找到方法,所以我發現有些情況需要多次調用get [Declared]方法。另外,我只需要調用setAccessible(true)一次。我還在基地嗎? – Nicholas 2009-01-05 23:28:45

1

你一定要反映方法對象只有一次(可能作爲私有靜態),並將其用於調用,而不是每次都反映出來。除非在編譯時不知道名稱,否則不要打擾緩存映射。

如果在你的上下文中是明智的,你可以可能想掛起並重新使用參數數組對象(不要忘記在方法退出時將數組元素清零以防止臨時GC抑制)。

如果總是以相同的PARMS(可能性很小)調用,你可以掛到PARMS和(參數數組與它的值)重用他們。

除了其他原因已經給出的理由之外,我不會嘗試任何其他的東西。

5

我在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業務不反射加速/消除的基礎上,吸引了太多的開發者。

謝謝克里斯。

0

如果調用成本低於該方法中所發生成本的10%,則不值得擔心。

您可以通過在循環中運行10^6次來確定是否存在例程的內容。用秒錶計時,秒數轉換爲微秒。