JVM中的Stack和Frame


JVM執行Java程序時需要裝載各種數據,比如類型信息(Class)、類型實例(Instance)、常量數據(Constant)、本地變量等。不同的數據存放在不同的內存區中,這些數據內存區稱作“運行時數據區(Runtime Data Area)”。運行時數據區有這樣幾個重要區:JVM Stack(簡稱Stack或者虛擬機棧、線程棧、棧等),Frame(又稱StackFrame/棧幀、方法棧等),Heap(堆/GC堆,即垃圾收集的對象所在區)。下面簡單介紹一下Stack和Frame,對於Heap,請參考垃圾收集相關文章

概覽

單個線程內共享的區:PC Register/JVM Stack/Native Method Stack。
所有線程共享的區:Heap/Method Area/Runtime Constant Pool。

上圖:運行時數據區。重點是每個線程擁有的PCRegister/Stack以及線程共享的Heap以及常量池(ConstantPool)

 

 

上圖:線程棧(VM Statck/Stack)包含的棧幀(Frame)。重點是棧幀和它的結構,操作棧(OperandStack)以及常量池引用。

Stack

結構:{JVM Stack [Frame][Frame][Frame]... }。
JVM Stack在每個線程被創建時被創建,用來存放一組棧幀(StackFrame/Frame)。
JVM Statck的大小可以是固定的,也可以是動態擴展的。如果線程需要一個比固定大小大的Stack,會發生StackOverflowError;如果動態擴展Stack時沒有足夠的內存或者系統沒有足夠的內存為新線程創建Stack,發生OutOfMemoryError。

Frame

結構:{Frame [ReturnValue] [LocalVariables[][][][]...] [OperandStack [][][]...] [ConstPoolRef] }
每次方法調用均會創建一個對應的Frame,方法執行完畢或者異常終止,Frame被銷毀。一個方法A調用另一個方法B時,A的frame停止,新的frame被創建賦予B,執行完畢后,把計算結果傳遞給A,A繼續執行。

 

局部變量表
局部變量表的大小在編譯期就被確定。基元類型數據以及引用和返回地址(returnAddress)占用一個局部變量大小,long/double需要兩個。

Java代碼“int a=0;int b=1;int c=2;”對應的局部變量表如下:

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

Start: 變量偏移量。
Length: 作用域范圍長度。[Start,Start+Length)就是該變量的作用域。
Slot: 一個Slot能存儲32bit的數據類型、引用、返回地址,long/dobule需要兩個Slot。


操作棧(OperandStack)
Frame被創建時,操作棧是空的。操作棧的每個項可以存放JVM的各種類型數據,包括long/double。
操作棧有個棧深,long/double貢獻兩個棧深。
操作棧調用其它有返回結果的方法時,會把結果push到棧上。

Java代碼:

int a=1;
int b=2;
int c=a+b;

對應的指令:

0: iconst_1 // push 1到操作棧。大於5的int值會用到 bipush <i> 指令。
1: istore_0 // pop 頂元素,存儲到index=0的本地變量。
2: iconst_2 // push 2 到操作棧
3: istore_1 // pop棧頂元素,存儲到index=1的本地變量。
4: iload_0  // 把index=0的本地變量加載到棧頂
5: iload_1  // 把index=1的本地變量加載到棧頂
6: iadd     // 把棧頂兩個數pop出來相加,並把結果存放到棧頂
7: istore_2 // 結果存儲到index=2的本地變量

 

Reference


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM