每個方法從調(diào)用開始至執(zhí)行完成的過程,都對應(yīng)著一個棧幀在虛擬機棧里面從入棧到出棧的過程。
1.棧幀的概念
棧幀(Stack Frame)是用于支持虛擬機進行方法調(diào)用和方法執(zhí)行的數(shù)據(jù)結(jié)構(gòu),它是虛擬機運行時數(shù)據(jù)區(qū)中的虛擬機棧(Virtual Machine Stack)的棧元素。
2.棧幀中的元素
棧幀存儲了方法的局部變量表、操作數(shù)棧、動態(tài)連接和方法返回地址等信息。
2.1.局部變量表
局部變量表(Local Variable Table)是一組變量值存儲空間,用于存放方法參數(shù)和方法內(nèi)部定義的局部變量。在Java程序編譯為Class文件時,就在方法的Code屬性的max_locals數(shù)據(jù)項中確定了該方法所需要分配的局部變量表的最大容量。
2.2.操作數(shù)棧
操作數(shù)棧(Operand Stack)也常稱為操作棧,它是一個后入先出(Last In First Out,LIFO)棧。同局部變量表一樣,操作數(shù)棧的最大深度也在編譯的時候?qū)懭氲紺ode屬性的max_stacks數(shù)據(jù)項中。
操作數(shù)是運算符作用于的實體,是表達式中的一個組成部分,它規(guī)定了指令中進行數(shù)字運算的量 。
表達式是操作數(shù)與操作符的組合。
2.3.動態(tài)連接
每個棧幀都包含一個指向運行時常量池中該棧幀所屬方法的引用,持有這個引用是為了支持方法調(diào)用過程中的動態(tài)連接(Dynamic Linking)。Class文文件的常量池中存在大量的符號引用,字節(jié)碼中的方法調(diào)用指令就以常量池中指向方法的符號引用作為參數(shù)。這些符號引用一部分會在類加載階段或者第一次使用的時候就轉(zhuǎn)化為直接引用,這種轉(zhuǎn)化稱為靜態(tài)解析。另外一部分將在每一次運行期間轉(zhuǎn)化為直接引用,這部分稱為動態(tài)連接。
2.4.方法返回地址
無論采用何種退出方式,在方法退出之后,都需要返回到方法被調(diào)用的位置,程序才能繼續(xù)執(zhí)行,方法返回時可能需要在棧幀中保存一些信息,用來幫助恢復(fù)它的上層方法的執(zhí)行狀態(tài)。一般來說,方法正常退出時,調(diào)用者的PC計數(shù)器的值可以作為返回地址,棧幀中很可能會保存這計數(shù)器值。而方法異常退出時,返回地址是要通過異常處理器表來確定的,棧幀中一般不會保存這部分信息。
方法退出的過程實際上就等同于把當(dāng)前棧幀出棧,因此退出時可能執(zhí)行的操作有:恢復(fù)上層方法的局部變量表和操作數(shù)棧,把返回值(如果有的話)壓入調(diào)用者棧幀的操作數(shù)棧中,調(diào)整PC計數(shù)器的值以指向方法調(diào)用指令后面的一條指令等。
2.5.附加信息
虛擬機規(guī)范允許具體的虛擬機實現(xiàn)增加一些規(guī)范里沒有描述的信息到棧幀之中,例如與調(diào)試相關(guān)的信息,這部分信息完全取決于具體的虛擬機實現(xiàn)。