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。
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的本地變量