Java Runtime Data Area & 函數(shù)調(diào)用棧 JVM Stack & 棧幀 Stack Frame

更多 Java 虛擬機(jī)方面的文章,請(qǐng)參見(jiàn)文集《Java 虛擬機(jī)》


運(yùn)行時(shí)數(shù)據(jù)區(qū) Runtime Data Area

JVM 執(zhí)行 Java 程序時(shí)需要裝載各種數(shù)據(jù),比如類(lèi)型信息(Class)、類(lèi)型實(shí)例(Instance)、常量數(shù)據(jù)(Constant)、本地變量等。
不同的數(shù)據(jù)存放在不同的內(nèi)存區(qū)中,這些數(shù)據(jù)內(nèi)存區(qū)稱(chēng)作運(yùn)行時(shí)數(shù)據(jù)區(qū)(Runtime Data Area)

運(yùn)行時(shí)數(shù)據(jù)區(qū)有這樣幾個(gè)重要區(qū):

  • JVM Stack(簡(jiǎn)稱(chēng) Stack 或者虛擬機(jī)棧、線程棧、棧等)
  • Frame(又稱(chēng)StackFrame/棧幀、方法棧等)
  • Heap(堆/GC堆,即垃圾收集的對(duì)象所在區(qū))

單個(gè)線程內(nèi)共享的區(qū):

  • PC Register 寄存器
  • JVM Stack 虛擬機(jī)棧
  • Native Method Stack 本地方法棧

所有線程共享的區(qū):

  • Heap 堆
  • Method Area 方法區(qū),其中包含 Runtime Constant Pool 常量池
運(yùn)行時(shí)數(shù)據(jù)區(qū) Runtime Data Area

函數(shù)調(diào)用棧 JVM Stack

結(jié)構(gòu):
{JVM Stack [Frame][Frame][Frame]... }

Java 的函數(shù)調(diào)用棧就是 Java 虛擬機(jī)棧,它是線程私有的,與線程一同被創(chuàng)建,用于存儲(chǔ)棧幀 Stack Frame,如下圖所示。

線程棧(VM Statck/Stack)包含的棧幀(Frame)

ThrowablegetStackTrace() 可以返回當(dāng)前線程的虛擬機(jī)棧信息,返回?cái)?shù)組的第一個(gè)元素是棧頂元素,最后一個(gè)元素是棧底元素

示例代碼如下:

public class StackTrace_Test {
    public void function1() {
        function2();
    }

    public void function2() {
        function3();
    }

    public void function3() {
        Throwable ex = new Throwable();

        StackTraceElement[] stackElements = ex.getStackTrace();

        System.out.println("Stack Length: " + stackElements.length);

        for (StackTraceElement stackTraceElement : stackElements) {
            System.out.println("Method Name: "
                    + stackTraceElement.getMethodName() + " Line Number: "
                    + stackTraceElement.getLineNumber());
        }
    }

    public static void main(String[] args) {
        StackTrace_Test t = new StackTrace_Test();
        t.function1();
    }
}

輸出如下:

Stack Length: 4
Method Name: function3 Line Number: 11
Method Name: function2 Line Number: 7
Method Name: function1 Line Number: 3
Method Name: main Line Number: 26

棧幀 Stack Frame

結(jié)構(gòu):
{Frame [ReturnValue] [LocalVariables[ ][ ][ ][ ]...] [OperandStack [ ][ ][ ]...] [ConstPoolRef] }

每次方法調(diào)用均會(huì)創(chuàng)建一個(gè)對(duì)應(yīng)的 Frame,方法執(zhí)行完畢或者異常終止,F(xiàn)rame 被銷(xiāo)毀。
一個(gè)方法 A 調(diào)用另一個(gè)方法 B 時(shí),A 的 Frame 停止,新的 Frame 被創(chuàng)建賦予 B,執(zhí)行完畢后,把計(jì)算結(jié)果傳遞給 A,A 繼續(xù)執(zhí)行。

局部變量表 LocalVariables

局部變量表的大小在編譯期就被確定,這些值并放在class文件中。

局部變量區(qū)被組織成一個(gè)以字長(zhǎng)為單位、從 0 開(kāi)始計(jì)數(shù)的數(shù)組

字節(jié)碼指令通過(guò)以 0 開(kāi)始的索引來(lái)使用其中的數(shù)據(jù)。

  • 類(lèi)型為 int、float、refence(引用)和returnAdress(方法出口)的值在數(shù)組內(nèi)只占據(jù)一項(xiàng)
  • 類(lèi)型為 byte、short、char 的值在存入數(shù)組時(shí)都先轉(zhuǎn)換為 int 值,因此同樣只占據(jù)一項(xiàng)
  • 類(lèi)型為 long 和 double 的值在數(shù)組中卻占據(jù)了連續(xù)兩項(xiàng)。

局部變量區(qū)包含了對(duì)應(yīng)的 方法參數(shù) 和 局部變量!!!

Java代碼 int a=0; int b=1; int c=2; 對(duì)應(yīng)的局部變量表如下:

LocalVariableTable:
Start Length Slot Name Signature
2 12 0 a I
4 10 1 b I
6 8 2 c I

其中:

  • Start 變量偏移量。
  • Length 作用域范圍長(zhǎng)度。[ Start, Start + Length )就是該變量的作用域。
  • Slot 一個(gè)Slot能存儲(chǔ) 32bit 的數(shù)據(jù)類(lèi)型、引用、返回地址,long / dobule需要兩個(gè)Slot。

操作數(shù)棧 OperandStack

操作數(shù)棧 OperandStack 的大小在編譯期就被確定,這些值并放在class文件中。

Frame 被創(chuàng)建時(shí),操作棧是空的。操作棧的每個(gè)項(xiàng)可以存放 JVM 的各種類(lèi)型數(shù)據(jù),包括 long / double。
操作棧有個(gè)棧深,long / double貢獻(xiàn)兩個(gè)棧深。
操作棧調(diào)用其它有返回結(jié)果的方法時(shí),會(huì)把結(jié)果 push 到棧上。

當(dāng)虛擬機(jī)調(diào)用一個(gè)方法時(shí),它從對(duì)應(yīng)的類(lèi)的類(lèi)型信息得到 局部變量表 LocalVariables 和 操作數(shù)棧 OperandStack 的大小,并據(jù)此分配棧幀內(nèi)存,并壓入 JVM Stack 中。


引用:
JVM中的Stack和Frame

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容