1. 程式人生 > >JAVA虛擬機器學習總結——虛擬機器位元組碼執行引擎

JAVA虛擬機器學習總結——虛擬機器位元組碼執行引擎

執行時的棧幀結構

棧幀是用於支援虛擬機器進行方法呼叫和方法執行的資料結構,它是虛擬機器執行時資料區中的虛擬機器棧的棧元素。
棧幀儲存了方法的區域性變量表,運算元棧,動態連線和方法返回值等資訊每一個方法從呼叫至執行完成的過程,都對應者棧幀在虛擬機器棧裡面從入找到出棧的過程。
在編譯程式程式碼的時候,棧幀需要多大的區域性變量表,多深的運算元棧都已經完全確定,並且寫入到了Code屬性之中,因此一個棧幀需要分配多少記憶體,不會受到程式執行期變數資料的影響,而僅僅取決於具體的虛擬機器實現。

區域性變量表

在編譯完成後,就將最大容量寫入到了Code屬性的max locals 資料項中。一組變數值儲存空間,用於存放方法引數和方法內部定義的區域性變數。
其容量以變數槽(Slot)為最小單位,每個Slot都應該能夠存放boolean, byte, char,short, int, float, reference, returnAddress, reference 表示一個物件例項的引用。對於long和double,虛擬機器會以高位對其的方式為其分配兩個Slot (區域性變量表建立線上程的堆疊上,是執行緒私有的,不會引起執行緒安全問題)。
虛擬機器通過索引定位的方式使用區域性變量表,索引值得範圍是從0開始至區域性變量表最大的slot數量。

對於類變數和例項變數,都有一個初始化為零值的階段,如int初始化為0,但是區域性變數沒有這個過程,所以在使用之前必須顯示地賦值。

運算元棧

在編譯時會將運算元棧的最大深度寫入到Code屬性的max stacks 資料項中。棧內容可以是任意的Java資料類,包括long和double, 32 位資料型別佔一個棧容量,64位佔2個。
棧中元素的資料型別必須與位元組碼指令的序列嚴格匹配。
虛擬機器會優化處理,讓兩個棧幀有一部分是重疊的,讓下面棧幀的部分運算元棧與上面校幀的區域性處量表重疊在一起,這樣可以共享資料,無需進行額外的引數賊值和傳遞。

動態連線

Class檔案的常量池中存在著大量的符號引用,位元組碼中的方法呼叫指令就以常量池中指向方法的符號引用為引數,這些符號引用一部分會在類載入階段或者一次使用的時候就轉化為直接引用,這種轉化稱為靜態解析。另外一部分將在每次執行期間轉化為直接引用,這部分稱為動態連結。

方法返回地址

  • 正常完成出口:執行引擎遇到任意一個方法返回的位元組碼指令,會有返回值傳遞給方法的呼叫者。
  • 異常完成出口:在方法執行時碰到異常,並且這個異常沒有得到處理。

方法退出的過程實際上就等同於把當前棧幀出棧,因此退出時可能執行的操作有:恢復上層方法的佈局變量表和運算元棧,把返回值壓入呼叫者棧幀的運算元棧,調整PC計數器的值以指向方法呼叫指令後面的一條指令。

方法呼叫

確定被呼叫方法的版本。

1.解析

一個靜態的過程,在編譯期間就已經確定,在類裝載的解析階段就會把涉及的符號引用全部轉變為可確定的直接引用,不會延遲到執行期間再去完成。包括靜態方法,私有方法,例項構造器,父類方法,final 修飾的方法。

2.分派(過載)

靜態分派

所有依賴靜態型別來定位執行版本的分派動作。編譯器在過載時是通過引數的靜態型別而不是實際型別作為判斷的依據。
程式在呼叫時必須顯示地指定字面量的靜態型別,否則會提示型別模糊,拒絕編譯。
在方法過載時,如果同時出現兩個引數分別為Serializable和Comparable的過載方法,他們的級別是一樣的,此時有型別模糊變長引數的優先級別是最低的。

動態分派(重寫):

在執行期間根據實際型別確定方法執行版本的分派過程。單分派和多分派:方法的接收者和方法的引數統稱為宗量。單分派是根據一個宗量對目標方法進行選擇,多分派是根據多餘一個宗量對目標方法進行選擇。

虛擬機器動態分派的實現

為類在方法區建立一個虛方法表,使用虛方法表索引來代替元資料查詢以提高效能。一般在類載入的連線階段進行初始化,準備了類的變數初始值後,虛擬機器會把該類的方法表業初始化完畢。具有相同簽名的方法,在子類,父類的虛方法表中應具有相同的索引序號。虛方法表中存放著各個方法的實際入口地址,如果某個方法在子類中沒有被重寫,那在子類的實際入口地址和父類的該方法一樣,如果子類重寫了某個方法,子類方法表中的地址將會替換為指向子類實際版本的入口地址。

MethodHandle與Reflection:

都是可以動態確定目標方法的機制。
+ Reflection是模擬java程式碼層次的方法呼叫,而MethodHandle是模擬位元組碼層次的方法呼叫。
+ Reflection中的java.lang.reflect.Method 物件遠比MethodHandle機制中的java.lang.invoke.MethodHandle物件所包含的資訊多。Reflection 是重量級的,MethodHandle是輕量級的。
+ MethodHandle是對位元組碼方法指令呼叫的模擬,所以可以做諸如方法內聯的優化。

基於棧的位元組碼解釋執行引擎

Java 編譯器輸出的指令流,基本上是一種基於棧的指令集架構,指令流中的大部分都是零地址指令。
基於棧的指令集優點:可移植性好,程式碼相對緊淡,編譯實現更加簡單。
缺點:由於指令數量和記憶體訪問的原因,執行速度相對梢慢,(完成相同功能所需指令數量一般比暫存器架構多, 因為出校入棧本身就產生了很多指令數量,而頻繁的棧訪問也就意味者頻繁的記憶體訪問。