JVM(8)-虛擬機器位元組碼執行引擎
在活動執行緒中,只有棧頂的棧時有效的,稱為當前棧幀 ,與這個棧幀相關聯的方法稱為當前方法 。下面對棧幀的4個主要部分進行分析。
區域性變量表
存放方法引數和方法內部定義的區域性變數
一些細節:
- 最小儲存單元(Slot),一個Slot可以存32位以內的資料型別。
-
boolean、byte、char、short、int、float、reference、returnAddress
佔一個Slot;long、double
佔兩個Slot,是非原子的,但它是執行緒安全 的,因為它是棧中的,是執行緒私有的。 -
對於例項方法,第一個Slot是傳遞所屬物件例項的引用,也就是我們常用的
this
。 - 注意區域性變數沒有初始值 哦。
- 當一個變數的pc暫存器的值大於Slot的作用域時,Slot是可以複用 的。
運算元棧
- 虛擬機器位元組碼執行引擎是“基於棧的執行引擎 ”,這個棧就是運算元棧。
- 對比基於暫存器 的執行引擎
優點:可移植性、程式碼更加緊湊、編譯器實現更緊湊。確定就是速度更慢。
- 下面棧幀的部分運算元棧與上面的棧幀的部分區域性變數是重疊的。
動態連線
指向執行時常量池中該棧幀所屬方法的引用,這個引用的為了支援方法呼叫過程的動態連線。具體內容在下面的方法呼叫中解釋。
方法返回地址
方法退出(也就是當前棧幀出棧 )的兩種方式:
return
方法呼叫
方法呼叫不等同於方法的執行,方法呼叫階段唯一的任務就是確定被呼叫方法的版本。說白了就是找方法,方法唯一就直接確定(解析 )。方法不唯一:過載(靜態分配)、重寫(動態分配)
解析呼叫
-
呼叫目標在程式程式碼寫好、編譯器進行編譯時就確定好的。這類方法時呼叫稱為解析。是靜態的。
-
非虛方法:靜態方法、私有方法、例項構造器、父類方法、final方法。它們都是採用解析呼叫。反之其它就是虛方法。
分派呼叫
Human man = new Man();
Human
是靜態型別(外觀型別),Man
是實際型別
靜態分派
依賴靜態型別來定位方法執行的版本的分配動作稱為靜態分配。最典型的應用是方法過載。
- 編譯器在過載 時是根據引數的靜態型別 作為依據的。
- 由於字面量沒有顯式的靜態型別,它過載時可能會有多種選擇,只是選一個更好的版本。
- 靜態分配和解析不是互斥的,例如靜態方法也是可以過載的。
- 注意:靜態分配更嚴格,一定是要用靜態型別做引數 。
動態分派
依賴實際型別來定位方法執行的版本的分配動作稱為動態分配。最典型的應用是方法重寫。
- 虛擬器在重寫 時是物件的實際型別 作為依據的。
- 注意:動態分配更寬鬆,如果實際型別中沒有對應的方法,就會向上找父類裡的相同方法來呼叫。
一個測試例子:
public class MixTest { static class Human{ } static class Man extends Human{} static class Woman extends Human{} public static class Father { public void choice(Human arg) { System.out.println("father choose human"); } public void choice(Man arg) { System.out.println("father choose man"); } public void choice(Woman arg) { System.out.println("father choose woman"); // 和同一類裡的同名方法是過載關係 } } public static class Son extends Father { public void choice(Human arg) { System.out.println("son choose human"); } public void choice(Man arg) { System.out.println("son choose man"); // 和父類的同名同參數方法是重寫關係 } public void choice(Woman arg) { System.out.println("son choose woman"); } } public static void main(String[] args) { Human woman = new Woman(); Man man = new Man(); Father father = new Father(); Father son = new Son(); father.choice(woman); // 重寫:物件型別選Father(實際型別) 過載:引數型別選 Human(靜態型別) son.choice(man); // 重寫:物件型別選Son(實際型別) 過載:引數型別選 Man(靜態型別) } } /* 程式輸出: father choose human son choose man */ 複製程式碼