簡體   English   中英

沒有類型信息的JVM invokeinterface

[英]JVM invokeinterface without type information

我目前正在使用Java ASM5生成一些代碼,我想知道為什么我可以對我的參數調用接口方法,該參數僅聲明為java / lang / Object類型。

MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "test", "(Ljava/lang/Object;)V", null, null);
mv.visitVarInsn(ALOAD, 0);
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKEINTERFACE, "org/mydomain/Foo", "foo", "()V", true);
mv.visitMethodInsn(INVOKEINTERFACE, "org/mydomain/Bar", "bar", "()V", true);
mv.visitInsn(RETURN);
mv.visitMaxs(-1,-1);
mv.visitEnd();

通常,我希望該代碼在調用該方法之前需要進行額外的強制轉換,以確保此對象確實實現了此接口。 只要我保證此Object確實實現了此接口,就可以安全地調用方法而無需進行其他強制轉換,還是可以遇到一些陷阱? VM的類型檢查似乎並不在乎。 如果使用Object調用它,該對象沒有實現該接口,則會得到java.lang.IncompatibleClassChangeError,這並不奇怪。 我可能會失去性能嗎?

JVM規范來看,只要您傳入的對象實現該接口, invokeinterface指令就可以正常工作。

您不會有任何性能上的損失,因為invokeinterface指令將檢查類型和方法簽名,即使它checkcast checkcast指令也是如此- 這是JVM源,它進行檢查以供參考

您遇到了HotSpot的Verifier的已知草率,而不是為了驗證invokeinterface調用的接收器類型的類型兼容性。 但這並不意味着此類代碼在形式上是正確的。

JVMSpec,第4.9.2節“結構約束”指出:

  • 作為方法調用指令目標的每個類實例的類型必須與該指令中指定的類或接口類型(JLS§5.2)兼容。

但是,存在一個實際問題,即按照算法(現在稱為“通過類型推斷進行驗證”)對較早的JVM的驗證者進行靜態驗證,該算法仍然是版本低於50的類文件的標准。 此答案解釋了該問題。 如果必須在分支后合並兩種不同的引用類型,則可能導致通用的超級類型無法實現接口,而兩種類型實際上都可以實現。 因此,拒絕后續的invokeinterface調用可能導致拒絕正確的代碼。

從版本50開始,存在“通過類型檢查進行驗證”,對於版本高於50的版本,甚至必須執行此操作。它使用堆棧映射表,顯式聲明類型合並的假定結果,從而消除了昂貴的操作。 因此,“通過類型檢查進行驗證”具有正式的規則 ,該規則要求靜態類型與接口類型兼容:

invokeinterface

如果滿足以下所有條件,則invokeinterface指令是安全的:

...

  • 人們可以有效地替換類型相匹配的類型MethodIntfName和中給出的參數類型Descriptor與在給定的返回類型輸入操作數堆棧上Descriptor ,得到的出射型的狀態。

(請注意,這些規則是相同的一個規則invokevirtual指令 ,不同之處在於它使用MethodIntfName代替MethodClassName

附帶說明一下,該代碼在java/lang/Object恰好同時實現org/mydomain/Fooorg/mydomain/Bar的環境中是正確的。 僅缺少針對實際環境的驗證。

簡而言之,您的代碼由於缺少檢查而碰巧可以在HotSpot上工作,但是卻不可移植,即可能在其他JVM上失敗,甚至在以后的HOTSpot版本中(執行類型檢查規則)也可能失敗。

省略checkcast沒有性能優勢,因為將以一種或另一種方式進行檢查,如果類型不匹配,則將拋出throwable。 因此,即使該特定的JVM不強制執行,我也會堅持創建形式正確的代碼。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM