目錄
一、棧幀
1. 棧幀結構
2.?基于棧的解釋執行過程
二、方法調用
1. 方法調用指令
2. 分派
三、動態類型語言
四、參考資料
一、棧幀
1. 棧幀結構
????????棧幀是Java虛擬機棧進行方法調用和執行的數據結構,是方法最基本的執行單元,是棧的元素。一個棧幀分配多大內存,則不受運行期影響,由程序源碼和占內存布局決定(內存大小編譯期可知)。每一個方法的調用開始和結束,都對應JVM棧的元素(棧幀)的入棧和出棧。
????????處于Java虛擬機棧頂的棧幀,稱為“當前棧幀”;當前棧幀所關聯的方法,稱為“當前方法”。從Java程序層面看,同一時刻、同一線程里,棧的所有方法都處于執行狀態;從執行引擎層面看,只有處于棧頂的方法才是執行狀態。
? ? ? ? 棧幀的結構包含:局部變量表、操作數棧、動態連接、返回地址、附加信息,如下表所示。注意:方法調用時,用局部變量表完成參數值到參數變量列表的傳遞過程,即:實參到形參的傳遞。
棧幀結構 | 作用 | 特點 |
局部變量表 (Local?Variables?Table) | 變量值的存儲空間 | 1.存儲:方法參數及方法定義的局部變量; 2.局部變量表容量以變量槽(slot)為最小單位; 3.方法Code屬性max_locals決定最大容量(slot數量); 4.每個slot都能存儲boolean、byte、char、short、int、float、returnAddress類型的數據; 5.64位JVM時,只有long、double會以高位對齊的方式分配兩個連續的slot空間; 6.實例方法時,第0位slot是this參數;其他:方法參數、方法局部變量的順序; 7.slot被回收重用的根本原因:slot中是否還存有對象的引用或值。 |
操作數棧 (Operand?Stack) | 操作數據的棧 | 1.JVM解釋執行引擎稱為“基于棧的操作引擎”; 2.棧的最大深度由編譯器Code屬性的max_stacks決定; 3.字節碼指令往棧中寫入和讀取操作數,即:操作數的入棧和出棧; 4.棧中元素數據類型與字節碼指令的類型必須嚴格匹配; 5.模型中JVM棧的元素(棧幀)是相互獨立的,但是JVM實際優化時,可能會有重疊區域。 |
動態連接 (Dynamic?Linking) | 棧幀所屬方法的引用 | 1.每個棧幀都包含一個指向常量池該棧幀所屬方法的引用(反過來,字節碼的方法調用以常量池的符號引用作為參數); 2.靜態解析:編譯器符號引用轉直接引用; ??動態連接:每次運行時符號引用轉直接引用。 |
返回地址 (Return?Address) | 返回給方法調用者 | 1.兩種方式退出當前方法: ??“正常調用完成”:執行引擎遇到方法的返回指令; ??“異常調用完成”:當前方法的異常表中沒有搜索到匹配的異常,則異常退出(不會有任何返回值); 2.方法正常退出時,一般主調方法的PC計數值可作為返回地址,若有返回值則壓入主調方法的棧幀中; |
附加信息 | 其他信息 | 如調試、性能收集相關的信息 |
????????其中,reference數據類型的作用: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
- 根據直接引用或間接引用查找到堆中實例對象的起始地址或索引; ??
- 根據直接引用或間接引用查找到元空間(方法區)的類型信息(反射機制獲取Class信息);?
2.?基于棧的解釋執行過程
????????指令集架構分類如下,Java是基于棧的指令集架構。
- 基于棧的指令集架構:字節碼指令流大部分都是零地址指令,依賴操作數棧工作,其優點:可移植; ??
- 基于寄存器的指令集架構:依賴于寄存器來訪問和存儲數據,如:主流PC機(x86二地址指令集)
????????執行字節碼有兩種選擇(或兩著兼備):解釋執行(解釋器)、編譯執行(即時編譯產生本地機器代碼),下面是基于棧的解釋執行過程實例、源碼和class文件如下。指令含義參考《HotSpot虛擬機之Class文件及字節碼指令》。
public int calc() {int a = 100;int b = 100;int c = 100;return (a + b) * c;
}
public int calc();descriptor: ()Iflags: ACC_PUBLICCode:stack=2, locals=4, args_size=10: bipush 1002: istore_13: bipush 1005: istore_26: bipush 1008: istore_39: iload_110: iload_211: iadd12: iload_313: imul14: ireturnLineNumberTable:line 29: 0line 30: 3line 31: 6line 32: 9LocalVariableTable:Start Length Slot Name Signature0 15 0 this Lcom/cmmon/instance/classLoad/NotInitialization;3 12 1 a I6 9 2 b I9 6 3 c I
二、方法調用
1. 方法調用指令
????????方法調用階段唯一目的是確定被調用方法的版本,即:調用哪個方法,其指令有5種:invokestatic、invokespecial、invokevirtual、invokeinterface、invokedynamic,如下表所示。
方法調用指令 | 特點 |
invokestatic | 作用:調用靜態方法; |
invokespecial | 作用:調用實例構造器<init>()方法、私有方法、父類中的方法; |
invokevirtual | 1.作用:調用所有的虛方法; 2.該指令:動態分派實現方法復寫。 |
invokeinterface | 作用:調用接口方法,運行時確定一個實現該接口的方法; |
invokedynamic | 1.作用:動態調用方法; 2.運行時動態解析動態解析調用點限定符所引用的方法,再去執行; |
注意: ???a.invokestatic、invokespecial、invokevirtual、invokeinterface其分派邏輯固化在JVM內部;而invokedynamic分派邏輯由用戶設定的引導方法來決定; ???b.只要能被invokestatic、invokespecial調用的方法,在編譯器可以確定方法版本,如:靜態、實例構造器<init>()、私有、父類、final修飾的方法; ???c.final修飾的方法雖然用invokevirtual調用,但它是非虛方法。 |
????????invokevirtual指令不僅把常量池的方法符號引用解析為直接引用,同時根據方法接收者的實際類型來選擇方法版本。invokevirtual指令解析過程,如下步驟:
- step1:找到操作數棧頂元素指向的對象的實際類型C;
- step2:類型C中查找與當前常量池匹配的方法時,且訪問權限校驗,通過則返回該方法,查找結束;否則拋出異常:java.lang.IllegalAccessError;
- step3:否則,從下往上的繼承關系對C的父類,進行搜索和驗證;
- step4:否則,沒有查找到匹配的方法,拋出異常:java.lang.AbstractMethodError。
2. 分派
????????分派揭示Java多態性的實現,即:如重載、重寫是如何實現。其分類:靜態分派、動態分派或是單分派、多分派,如下表所示。
分派分類 | 概念 | 應用 | 特點 |
靜態分派 | 所有依賴靜態類型來決定方法版本的分派動作 | 方法重載 | 1.靜態分派發生在編譯期; 2.重載版本多個時,選擇最合適的順序:自動類型轉換、自動裝箱、裝箱實現接口、父類(從下往上)、可變參數方法。 |
動態分派 | 運行期根據實際類型來決定方法版本的分派動作 | 方法重寫 | 1.動態分派發生在運行期; 2.由invokevirtual指令實現。 |
注意: ???a.只有方法有多態,而字段永遠不參與多態; ???b.變量的“靜態類型”和“實際類型”:(都可能變化,但是變化時期不同) ? ? ? 靜態類型:變化在使用時發生,且變量本身的類型不會改變,且最終在編譯期可知; ? ? ? 實際類型:變化結果在運行期確定,編譯期不知道一個對象的實際類型是什么; ???c.類型“寬化轉換”:chart?>?int?>?long?>?float?>?double(不會匹配byte、short類型); ???d.“方法的宗量”:方法接收者、方法的參數: ? ? ? ?單分派:根據一個宗量對目標方法進行選擇; ? ? ? ?多分派:根據多于一個宗量對目標方法進行選擇。 |
? ? ? ? 注意:方法重寫的本質是不僅把常量池的方法符號引用解析為直接引用,同時根據方法接收者的實際類型來選擇方法版本;字段不參與多態,但子類聲明與父類的相同的字段時,子類內存中兩個字段都會存在,但子類會遮蔽父類的同名字段。
? ? ? ? 方法重寫實現多態,在運行時頻繁從元數據進行查找,解決該問題的方法:invokevirtual的虛方法表;invokeinterface的接口方法表。虛方法表目的是存儲該類各個方法的實際入口地址,如下所示其特點。
三、動態類型語言
????????JDK7增加了動態類型指令invokedynamic,其類型檢查的主過程在運行期,而不是編譯期。
四、參考資料
HotSpot虛擬機之Class文件及字節碼指令_愛我所愛0505的博客-CSDN博客
HotSpot虛擬機之類加載過程及類加載器_愛我所愛0505的博客-CSDN博客
百度安全驗證
【Java 虛擬機原理】棧幀 | 動態鏈接 | 方法區 | 字節碼文件二進制分析-騰訊云開發者社區-騰訊云
Java虛擬機運行時棧幀結構_java棧幀_愛躺平的咸魚的博客-CSDN博客
【Java -- 虛擬器】方法分派模型 -- 靜態分派、動態分派_51CTO博客_java虛擬器
Java中的靜態分派和動態分派原理_51CTO博客_靜態分派