JVM--運(yùn)行時(shí)棧幀結(jié)構(gòu)

棧幀(Stack Frame)是用于支持虛擬機(jī)進(jìn)行方法調(diào)用和方法執(zhí)行的數(shù)據(jù)結(jié)構(gòu),它是虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū)的虛擬機(jī)棧(Virtual Machine Stack)的棧元素。棧幀存儲(chǔ)了方法的局部變量表,操作數(shù)棧,動(dòng)態(tài)連接和方法返回地址等信息。第一個(gè)方法從調(diào)用開(kāi)始到執(zhí)行完成,就對(duì)應(yīng)著一個(gè)棧幀在虛擬機(jī)棧中從入棧到出棧的過(guò)程。

每一個(gè)棧幀都包括了局部變量表,操作數(shù)棧動(dòng)態(tài)連接,方法返回地址一些額外的附加信息。在編譯代碼的時(shí)候,棧幀中需要多大的局部變量表,多深的操作數(shù)棧都已經(jīng)完全確定了,并且寫(xiě)入到了方法表的Code屬性中,因此一個(gè)棧幀需要分配多少內(nèi)存,不會(huì)受到程序運(yùn)行期變量數(shù)據(jù)的影響,而僅僅取決于具體虛擬機(jī)的實(shí)現(xiàn)。

一個(gè)線程中的方法調(diào)用鏈可能會(huì)很長(zhǎng),很多方法都同時(shí)處理執(zhí)行狀態(tài)。對(duì)于執(zhí)行引擎來(lái)講,活動(dòng)線程中,只有虛擬機(jī)棧頂?shù)臈攀怯行У?,稱為當(dāng)前棧幀(Current Stack Frame),這個(gè)棧幀所關(guān)聯(lián)的方法稱為當(dāng)前方法(Current Method)。執(zhí)行引用所運(yùn)行的所有字節(jié)碼指令都只針對(duì)當(dāng)前棧幀進(jìn)行操作。棧幀的概念結(jié)構(gòu)如下圖所示:


1. 局部變量表

局部變量表(Local Variable Table)是一組變量值存儲(chǔ)空間,用于存放方法參數(shù)和方法內(nèi)部定義的局部變量。在編譯Class文件時(shí),就在方法的Code屬性的max_locals數(shù)據(jù)項(xiàng)中已經(jīng)確定了該方法需要分配的局部變量表的最大容量。

變量槽 (Variable Slot)是局部變量表的最小單位,沒(méi)有強(qiáng)制規(guī)定大小為 32 位,雖然32位足夠存放大部分類型的數(shù)據(jù)。一個(gè) Slot可以存放 boolean、byte、char、shortint、float、referencereturnAddress 8種類型。其中 reference 表示對(duì)一個(gè)對(duì)象實(shí)例的引用,通過(guò)它可以得到對(duì)象在Java 堆中存放的起始地址的索引和該數(shù)據(jù)所屬數(shù)據(jù)類型在方法區(qū)的類型信息。returnAddress則指向了一條字節(jié)碼指令的地址。 對(duì)于64位的 long 和 double 變量而言,虛擬機(jī)會(huì)為其分配兩個(gè)連續(xù)的 Slot 空間

虛擬機(jī)通過(guò)索引定位的方式使用局部變量表。之前我們知道,局部變量表存放的是方法參數(shù)和局部變量。當(dāng)調(diào)用方法是非static 方法時(shí),局部變量表中第0位索引的 Slot 默認(rèn)是用于傳遞方法所屬對(duì)象實(shí)例的引用,即 “this” 關(guān)鍵字指向的對(duì)象。分配完方法參數(shù)后,便會(huì)依次分配方法內(nèi)部定義的局部變量。

Slot復(fù)用驗(yàn)證

為了節(jié)省棧幀空間,局部變量表中的 Slot 是可以重用的。當(dāng)離開(kāi)了某些變量的作用域之后,這些變量對(duì)應(yīng)的 Slot 就可以交給其他變量使用。這種機(jī)制有時(shí)候會(huì)影響垃圾回收行為。

public class Main {
    public static void main(String[] args) {
        byte[] placeholder = new byte[64*1024*1024];
        System.gc();
    }
}
[GC (System.gc())  69468K->66384K(188416K), 0.0016481 secs]
[Full GC (System.gc())  66384K->66280K(188416K), 0.0079337 secs]
public class Main {
    public static void main(String[] args) {
        {
            byte[] placeholder = new byte[64*1024*1024];
        }
        int a = 0;
        System.gc();
    }
}
[GC (System.gc())  69468K->66368K(188416K), 0.0012876 secs]
[Full GC (System.gc())  66368K->744K(188416K), 0.0055897 secs]

可以看到,當(dāng)我吧byte的聲明單獨(dú)放到代碼塊中,然后再執(zhí)行作用域之外的代碼的時(shí)候,gc對(duì)slot進(jìn)行了回收。

注意:jvm不會(huì)給局部變量賦初始值,只給全局變量賦初始值。

2. 操作數(shù)棧

操作數(shù)棧(Operand Stack)也常稱為操作棧,是一個(gè)后入先出棧。在Class 文件的Code 屬性的 max_stacks 指定了執(zhí)行過(guò)程中最大的棧深度。Java 虛擬機(jī)的解釋執(zhí)行引擎稱為”基于棧的執(zhí)行引擎“,這里的棧就是指操作數(shù)棧。

方法執(zhí)行中進(jìn)行算術(shù)運(yùn)算或者是調(diào)用其他的方法進(jìn)行參數(shù)傳遞的時(shí)候是通過(guò)操作數(shù)棧進(jìn)行的。

jvm對(duì)操作數(shù)棧的優(yōu)化

在概念模型中,兩個(gè)棧幀是相互獨(dú)立的。但是大多數(shù)虛擬機(jī)的實(shí)現(xiàn)都會(huì)進(jìn)行優(yōu)化,令兩個(gè)棧幀出現(xiàn)一部分重疊。令下面的部分操作數(shù)棧與上面的局部變量表重疊在一塊,這樣在方法調(diào)用的時(shí)候可以共用一部分?jǐn)?shù)據(jù),無(wú)需進(jìn)行額外的參數(shù)復(fù)制傳遞。

3. 動(dòng)態(tài)鏈接

每個(gè)棧幀都包含一個(gè)執(zhí)行運(yùn)行時(shí)常量池中該棧幀所屬方法的引用,持有這個(gè)引用是為了支持方法調(diào)用過(guò)程中的動(dòng)態(tài)連接(Dynamic Linking)。

Class 文件中存放了大量的符號(hào)引用,字節(jié)碼中的方法調(diào)用指令就是以常量池中指向方法的符號(hào)引用作為參數(shù)。這些符號(hào)引用一部分會(huì)在類加載階段或第一次使用時(shí)轉(zhuǎn)化為直接引用,這種轉(zhuǎn)化稱為靜態(tài)解析。另一部分將在每一次運(yùn)行期間轉(zhuǎn)化為直接引用,這部分稱為動(dòng)態(tài)連接

4. 方法返回地址

當(dāng)一個(gè)方法開(kāi)始執(zhí)行以后,只有兩種方法可以退出當(dāng)前方法

  1. 當(dāng)執(zhí)行遇到返回指令,會(huì)將返回值傳遞給上層的方法調(diào)用者,這種退出的方式稱為正常完成出口(Normal Method Invocation Completion),一般來(lái)說(shuō),調(diào)用者的PC計(jì)數(shù)器可以作為返回地址。
  2. 當(dāng)執(zhí)行遇到異常,并且當(dāng)前方法體內(nèi)沒(méi)有得到處理,就會(huì)導(dǎo)致方法退出,此時(shí)是沒(méi)有返回值的,稱為異常完成出口(Abrupt Method Invocation Completion),返回地址要通過(guò)異常處理器表來(lái)確定。

當(dāng)方法返回時(shí),可能進(jìn)行3個(gè)操作

  1. 恢復(fù)上層方法的局部變量表和操作數(shù)棧
  2. 把返回值壓入調(diào)用者調(diào)用者棧幀的操作數(shù)棧
  3. 調(diào)整 PC 計(jì)數(shù)器的值以指向方法調(diào)用指令后面的一條指令

5. 附加信息

虛擬機(jī)規(guī)范允許具體的虛擬機(jī)實(shí)現(xiàn)增加一些規(guī)范里沒(méi)有描述的信息到棧幀之中,例如與調(diào)試相關(guān)的信息,這部分信息完全取決于具體的虛擬機(jī)實(shí)現(xiàn)。在實(shí)際開(kāi)發(fā)中,一般會(huì)把動(dòng)態(tài)連接、方法返回地址與其他附加信息全部歸為一類,稱為棧幀信息。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容