JVM--虛擬機器位元組碼執行引擎--大致總結

2020-08-13 00:54:59

執行引擎在執行位元組碼的時候,通常會有解釋執行(通過直譯器執行)和編譯執行(通過即時編譯器編譯成原生代碼)

public statci void main(String[] args){
    {
        byte[] placeholder=new byte[64*1024×1024];
    }
    System.gc();
} 

該程式碼呼叫gc後並沒有回收placeholder的記憶體,原因是離開它的作用域後並沒有發生對區域性變數表的讀寫操作,變數槽還沒有被其他變數複用,所以作爲GC Roots一部分的區域性變數表仍保持着對他的關聯。將其設定爲null或在域後定義變數(如int i=0;)就可將其回收。

5條方法呼叫位元組碼指令:
1.invokestatic:用於呼叫靜態方法。
2.invokespecial:擁有呼叫範例構造器<init>方法、私有方法和父類別方法。
3.invokevirtual:用於呼叫所有的虛擬函式。
4.invokeinterface:用於呼叫介面方法
5.invokedynamic:先在執行時動態解析出調用點限定符所參照的方法,然後再執行。

只要能被iinvokestatic和invokespecial指令呼叫的方法,都可以在解析階段確定方法唯一的呼叫版本,符合這個的方法有靜態方法、私方法、範例構造器、父類別方法、,在加上final修飾的方法(雖然他使用invokevirtual指令),這些方法成爲虛方法,其他方法稱爲非虛方法。

分派

靜態分派
所有依賴靜態型別來決定方法執行版本的分派動作,都成爲靜態分派。如方法過載中,它是根據參數的靜態型別來確定呼叫的版本,而靜態型別是在編譯期就是可知的。
動態分派
invokevirtual指令執行的第一步就是在執行期間確定接受者的實際型別,再根據實際型別呼叫方法版本。
invokevirtual指令執行時解析大致分爲以下幾步:

  1. 找到運算元棧頂的所指向的物件的實際型別。
  2. 在型別中找到與常數中描述符和簡單名稱都相同的方法,進行存取許可權校驗,通過則返回這個方法的直接參照;不通過返回IllegalAccessError異常。
  3. 如果沒有找到該方法,則按照繼承關係從下往上一次對其的各個父類別進行第二步的搜尋和驗證過程。
  4. 沒有找到返回AbstractMethodError異常。

方法的接受者與方法的參數統稱爲方法的總量。

  • java靜態分派選擇目標方法的依據有兩點:一是靜態型別(接受者),二是方法參數。所以java靜態分派屬於多分派型別;
  • java動態分派選擇因素只有方法接受者,所以java動態分派屬於單分派型別。

動態分派執行是非常頻繁的動作,會對效能帶來一定的影響,常見的優化手段是爲型別在方法區建立一個虛方法表,與此對應的,在invokeinterface執行時也會用到介面方法表,使用虛方法表索引來代替元數據查詢以提高效能。
如果某個方法在子類中沒有被重寫,那子類的虛方法表中的地址入口和父類別相同方法的地址入口是一致的,都是指向父類別的實現入口。
爲了程式實現方便,具有相同簽名的方法在父類別,子類的虛方法表中應當具有相同的索引號,這樣當型別變化時僅需要變更查詢的虛方法表就可以找到不同方法的入口。
虛方法表一般在準備了類的變數初始化值後,虛擬機器會把其虛方法表也一同初始化。

動態型別語言的關鍵特徵是它的型別檢查的主體過程是在執行期而不是編譯期,如Erlang等。

MethodHandle在使用方法和效果上與Reflection區別

  1. 都是模擬方法呼叫,但Reflection是模擬java程式碼層次的方法呼叫,而MethodHandle是模擬位元組碼層次的方法呼叫。在MethodHandles.Looku上的三個方法findStatic()、findVirtual()、findSpecial()對應invokestatic、invokevirtual(及invokeinterface)和 invokespecial這幾個位元組碼指令的執行許可權校驗行爲,而這些Reflection不需要關心。
  2. java.lang.reflect.Method物件包含的資訊比MethodHandle物件多。前者是方法在java端的全面映象,而後者僅包含執行該方法的相關資訊。Reflection是重量級的,MethodHandle是輕量級的。
  3. 理論上虛擬機器在位元組碼上的優化,在MethodHandle可以採用類似的思路去支援,而Reflection在呼叫方法上幾乎不可能直接去實施各類呼叫點優化。
  4. Reflection的設計目標只爲java語言服務,而MethodHandle可服務於所有java虛擬機器之上的語言。

invokedynamic指令

  • 這條指令的參數不再是CONSTANT_Methodref_info常數,而是JDK7加入的CONSTANT_InvokeDynamic_info常數,該常數可以得到3項資訊:引導方法(該方法存放在新增的BootstrapMethods屬性中)、方法型別和名稱。
  • 引導方法是有固定的參數,並且返回值爲java.lang.CallSize物件,這個物件代表真正要執行的目標方法呼叫。
  • 過程是獲取引導方法的控制代碼,傳遞參數呼叫引導方法來建立含有目標方法的CallSite物件,然後該物件返回個invokedynamic指令來實現目標方法的呼叫。