每当启动一个新线程时,Java虚拟机都会为它分配一个Java栈。Java栈以帧为单位保存线程的运行状态。虚拟机只会直接对Java栈执行两个操作:以帧为单位的压栈或出栈。当线程调用一个Java方法时,虚拟机都会在该线程的Java栈中压入一个新栈。
栈帧:
栈帧由三部分组成:局部变量,操作数栈和栈数据区。
当虚拟机调用一个Java方法时,它从对应类的信息中得到此方法的局部变量和操作数据栈的大小,并据此分配栈帧内存,然后压入Java栈中。
1)局部变量:Java栈帧的局部变量区被组织为一个以字节为单位,从0开始计数的数组。
2)操作数栈:和局部变量一样,被组织成一个字长为单位的数组。通过标准的栈操作——压栈和出栈来访问。
3)栈数据区:Java栈中支持常量池解析,正常方法返回以及异常派发机制的一些信息。
例子:
一个斐波那契序列的代码,如下:
class Fibonacci {
static void calcSequence() {
long fiboNum = 1;
long a = 1;
long b = 1;
for (;;) {
fiboNum = a + b;
a = b;
b = fiboNum;
}
}
}
指令码:
0 lconst_1 // Push long constant 1
1 lstore_0 // Pop long into local vars 0 & 1: long a = 1;
2 lconst_1 // Push long constant 1
3 lstore_2 // Pop long into local vars 2 & 3: long b = 1;
4 lconst_1 // Push long constant 1
5 lstore 4 // Pop long into local vars 4 & 5: long fiboNum = 1;
7 lload_0 // Push long from local vars 0 & 1
8 lload_2 // Push long from local vars 2 & 3
9 ladd // Pop two longs, add them, push result
10 lstore 4 // Pop long into local vars 4 & 5: fiboNum = a + b;
12 lload_2 // Push long from local vars 2 & 3
13 lstore_0 // Pop long into local vars 0 & 1: a = b;
14 lload 4 // Push long from local vars 4 & 5
16 lstore_2 // Pop long into local vars 2 & 3: b = fiboNum;
17 goto 7 // Jump back to offset 7: for (;;) {}
虚拟机镜像:
1)初始化。Local Variables:局部变量。Operand Stack:操作数栈。
2)分配3个变量:fiboNum,a,b。局部变量是一个3位的数组。
3)lload_0:从局部变量0中装载long类型值,lload_2:从局部变量2中装载long类型值。
4)ladd:执行int类型的加法。
5)istore 4:从栈中弹出int类型值,然后将其存到位置为4的局部变量中。
参考资料:
《深入Java虚拟机》