一、定義
棧幀(stack frame)是用于支援虛拟機進行方法調用和方法執行的資料結構,它是虛拟機運作時資料區中的虛拟機棧的棧元素。棧幀存儲了方法的局部變量表、操作數棧、動态連接配接和方法傳回位址等資訊。
每一個方法從調用開始到執行完成的過程,就對應着一個棧幀在虛拟機棧裡面從入棧到出棧的過程。
對于執行引擎來說,活動線程中,隻有棧頂的棧幀是有效的,稱為目前棧幀,這個棧幀所關聯的方法稱為目前方法。執行引擎所運作的所有位元組碼指令都隻針對目前棧幀進行操作。

二、組成
-
局部變量表
局部變量表是一組變量值存儲空間,用于存放方法參數和方法内部定義的局部變量。在Java程式被編譯成Class檔案時,就在方法的Code屬性的max_locals資料項中确定了該方法所需要配置設定的最大局部變量表的容量。
局部變量表的容量以變量槽(Slot)為最小機關,32位虛拟機中一個Slot可以存放一個32位以内的資料類型(boolean、byte、char、short、int、float、reference和returnAddress八種。
reference類型虛拟機規範沒有明确說明它的長度,但一般來說,虛拟機實作至少都應當能從此引用中直接或者間接地查找到對象在Java堆中的起始位址索引和方法區中的對象類型資料。
系統不會為局部變量賦予初始值(執行個體變量和類變量都會被賦予初始值。也就是說不存在類變量那樣的準備階段。
-
操作數棧
Java虛拟機的解釋執行引擎被稱為"基于棧的執行引擎",其中所指的棧就是指-操作數棧。
操作數棧也常被稱為操作棧。
和局部變量區一樣,操作數棧也是被組織成一個以字長為機關的數組。但是和前者不同的是,它不是通過索引來通路,而是通過标準的棧操作—壓棧和出棧—來通路的。比如,如果某個指令把一個值壓入到操作數棧中,稍後另一個指令就可以彈出這個值來使用。
虛拟機在操作數棧中存儲資料的方式和在局部變量區中是一樣的:如int、long、float、double、reference和returnType的存儲。對于byte、short以及char類型的值在壓入到操作數棧之前,也會被轉換為int。
虛拟機把操作數棧作為它的工作區——大多數指令都要從這裡彈出資料,執行運算,然後把結果壓回操作數棧。比如,iadd指令就要從操作數棧中彈出兩個整數,執行加法運算,其結果又壓回到操作數棧中,看看下面的示例,它示範了虛拟機是如何把兩個int類型的局部變量相加,再把結果儲存到第三個局部變量的:
-
動态連接配接
在一個class檔案中,一個方法要調用其他方法,需要将這些方法的符号引用轉化為其在記憶體位址中的直接引用,而符号引用存在于方法區中的運作時常量池。
Java虛拟機棧中,每個棧幀都包含一個指向運作時常量池中該棧所屬方法的符号引用,持有這個引用的目的是為了支援方法調用過程中的動态連接配接(Dynamic Linking)。
這些符号引用一部分會在類加載階段或者第一次使用時就直接轉化為直接引用,這類轉化稱為靜态解析。另一部分将在每次運作期間轉化為直接引用,這類轉化稱為動态連接配接。
-
方法傳回
當一個方法開始執行時,可能有兩種方式退出該方法:
正常完成出口
異常完成出口
無論方法采用何種方式退出,在方法退出後都需要傳回到方法被調用的位置,程式才能繼續執行,方法傳回時可能需要在目前棧幀中儲存一些資訊,用來幫他恢複它的上層方法執行狀态。
方法退出過程實際上就等同于把目前棧幀出棧,是以退出可以執行的操作有:恢複上層方法的局部變量表和操作數棧,把傳回值(如果有的話)壓入調用者的操作數棧中,調整PC計數器的值以指向方法調用指令後的下一條指令。
一般來說,方法正常退出時,調用者的PC計數值可以作為傳回位址,棧幀中可能儲存此計數值。而方法異常退出時,傳回位址是通過異常處理器表确定的,棧幀中一般不會儲存此部分資訊。
三、示例位元組碼 a=b+c
//位元組碼
begin
iload_0 // push the int in local variable 0 onto the stack
iload_1 // push the int in local variable 1 onto the stack
iadd // pop two ints, add them, push result
istore_2 // pop int, store into local variable 2
end
在這個位元組碼序列裡,前兩個指令iload_0和iload_1将存儲在局部變量中索引為0和1的整數(b,c)壓入操作數棧中,其後iadd指令從操作數棧中彈出那兩個整數相加,再将結果壓入操作數棧。第四條指令istore_2則從操作數棧中彈出結果,并把它存儲到局部變量區索引為2(a)的位置。