2017-03-05 55 views
1

我一直在尋找到下面的java程序調用class.NewInstance()時爲什麼調用虛擬代替InvokeSpecial?

public class ASMPlayground { 
    private String bar; 

    public String getBar(){ 
     return bar; 
    } 

    public void setBar(String bar) throws IllegalAccessException, InstantiationException { 
     String name = String.class.newInstance(); 
     System.out.println(name); 
    } 

    public static void main(String[] args) { 

    } 
} 

下面的字節碼片段引起了我的眼睛,似乎次優的拆卸

LDC Ljava/lang/String;.class 
INVOKEVIRTUAL java/lang/Class.newInstance()Ljava/lang/Object; 
CHECKCAST java/lang/String 

問:

InvokeVirtual使用當要執行的方法依賴於對象引用時。鑑於「類」是final,newInstance()在「Class」中只存在 ,爲什麼不使用InvokeSpecial而不是InvokeVirtual?它不會更高性能嗎?

回答

3

InvokeSpecial用於指定是

超類,私人和實例的初始化方法調用

規格調用:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.invokespecial

Class.newInstance既不是超類的方法調用也不私有方法調用也不會調用初始化方法。這也不是一個動態的方法,InvokeDynamic在這裏無關。這兩條指令都不能用在這裏,因爲它會對抗jvms。在創建jvms時,有可能允許用「更高性能」的指令替換一些指令,但正如我在jvms中看到的那樣,它還沒有完成。

難道不是更高性能?

JIT足夠聰明地理解在這種情況下不需要遍歷虛擬方法表,所以它不應該減慢執行速度。實際表現應該通過實際測試進行比較,但我認爲沒有理由期望有顯着差異。

+1

特別是'Class'是'final',所以唯一的虛擬方法是對父類方法的調用。 –

+0

我做了一個編輯。 InvokedDynamic是一個錯字。我打算說InvokeVirtual。 – KodeWarrior

+0

我沒有查看InvokeSpecial的文檔。我也檢查了InvokeVirtual https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.invokevirtual 調用實例方法;基於類的派遣 仍然不明白爲什麼在這裏使用invokevirtual給定的類是final的。 可能的答案可能是InvokedSpecial操作碼只能用於相同/超類的方法。 – KodeWarrior

0

目標方法或類是final的事實決不會使用其他類/調用方法將編譯後的形式更改爲類。

這是由JLS,第13章「二進制兼容性」,§13.4.17, final methods規定:

改變這種聲明final不再被宣佈final不破與預先存在的二進制兼容性的方法。

同樣§13.4.2. final Classes

改變這種聲明final不再被宣佈final不破與預先存在的二進制兼容性的分類。

顯然,依賴目標方法的性質的調用指令會違反此規範。

預期沒有相關的性能影響。當符號引用必須解析爲實際的方法時(根據JVM的運行時表示),始終會有首次開銷。在這一點上,JVM還可以記錄這個方法或其聲明類實際上是final到調用指令的事實,如果有好處的話。現代JVM甚至更遠,例如利用這樣一個事實,即一個非final方法並沒有被實際覆蓋到相同的效果,儘管如果一個子類被加載和實例化,並且具有覆蓋方法,這些調用將需要去最優化。所以唯一的區別是final修飾符保證了這種去最佳化永遠不會是必需的。

+0

'final'真的能保證什麼嗎?可以在運行時刪除修飾符,並且在編譯期間可以替換假源,因爲類加載很懶,所以我認爲它應該被允許。你有任何鏈接說明否則? –

+0

@Sergey Fedorov:好的,正如我的答案的第一部分所解釋的,在課程加載之前,'final'根本不受* Binary Compatibility *規範的要求的影響。我的答案的最後一句與JVM相關,只有在類已經加載並且JVM開始優化時,纔會假設該方法永遠不會被覆蓋。然後,如果方法是「final」,JVM將拒絕重寫嘗試。但是,這仍然是JVM實現特有的。例如。如果一個JVM允許通過Instrumentation刪除'final',那麼它必須在這種情況下支持去優化。 – Holger

+0

afaik,hotspot jit支持去優化,但這不是重點。修飾符可以通過反射來移除,這不是特定於jvm的,並且至少適用於字段。到現在爲止,我認爲它應該與方法一起工作,所以在編譯完成後,「final」意味着什麼。你知道有什麼證據可以反證嗎? –

相關問題