更多 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 常量池
函數(shù)調(diào)用棧 JVM Stack
結(jié)構(gòu):
{JVM Stack [Frame][Frame][Frame]... }
Java 的函數(shù)調(diào)用棧就是 Java 虛擬機(jī)棧,它是線程私有的,與線程一同被創(chuàng)建,用于存儲(chǔ)棧幀 Stack Frame,如下圖所示。
Throwable
的 getStackTrace()
可以返回當(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