栈帧(Stack Frame)是 JVM 方法执行时的 最小单位,每个方法被调用时,JVM 都会在**虚拟机栈(JVM Stack)**中创建一个栈帧。
当方法执行完成后,栈帧出栈,方法调用结束。
栈帧组件 | 作用 |
---|---|
局部变量表(Local Variable Table) | 存放方法的参数 和 局部变量(基本类型、对象引用、returnAddress) |
操作数栈(Operand Stack) | 执行方法的操作数 存取,JVM 指令从这里取数、计算、存结果 |
动态链接(Dynamic Linking) | 维护常量池 中的符号引用到实际方法、字段的解析(指向方法区中的方法) |
返回地址(Return Address) | 记录当前方法调用者的地址(方法返回时,继续执行调用者的指令) |
附加信息(Additional Info) | 存放异常处理表、JIT 编译优化信息等 |
returnAddress
(用于 jsr
/ret
指令,主要用于 finally
处理)。static
方法的 第 0 号槽位是 this
。long
和 double
类型占两个槽位(64 位)。✅ 示例:方法的局部变量表
public void test(int a, long b) {
int c = a + 10;
double d = b * 2.0;
}
局部变量表布局:
索引 | 变量 | 类型 |
---|---|---|
0 | this |
test 实例 |
1 | a |
int |
2 | b |
long (占用索引 2 和 3) |
4 | c |
int |
5 | d |
double (占用索引 5 和 6) |
max_stack
指定)。✅ 示例:简单的字节码
public int add(int x, int y) {
return x + y;
}
对应的 Java 字节码(javap -c
)
0: iload_1 // x 入操作数栈
1: iload_2 // y 入操作数栈
2: iadd // x + y(操作数栈出两个数,相加,结果入栈)
3: ireturn // 返回操作数栈顶部的值
执行过程(操作数栈变化):
指令 | 操作数栈 |
---|---|
iload_1 |
[x] |
iload_2 |
[x, y] |
iadd |
[x + y] |
ireturn |
返回 x + y |
final
方法、private
方法、static
方法)。✅ 示例:多态方法调用
class Parent { void say() { System.out.println("Parent"); } }
class Child extends Parent { void say() { System.out.println("Child"); } }
public class Test {
public static void main(String[] args) {
Parent obj = new Child();
obj.say(); // 调用的是 Child 的 say() 方法
}
}
对应的 JVM 指令
invokevirtual #2 // #2 是 "say()" 方法的符号引用
执行过程:
Child
类的 say()
方法(因为 obj
实际是 Child
)。✅ 示例
public void methodA() {
methodB(); // 执行 methodB
System.out.println("A"); // methodB 执行完后,返回到这里
}
public void methodB() {
System.out.println("B");
}
返回地址作用:
方法 | 执行 |
---|---|
methodA |
先调用 methodB() |
methodB |
执行 println("B") ,然后返回 methodA |
methodA |
执行 println("A") |
栈帧部分 | 作用 |
---|---|
局部变量表 | 存参数、局部变量 |
操作数栈 | 计算数据存取 |
动态链接 | 解析方法符号引用 |
返回地址 | 记录调用者的返回位置 |
附加信息 | 存异常处理表等 |
栈帧是 JVM 运行时方法调用的核心,每个方法执行时都会创建栈帧,执行完后出栈。JVM 通过栈帧管理方法调用,支持递归、方法链调用、异常处理等功能。
✅ 一句话总结
栈帧是 JVM 运行时调用方法的基本单位,每个方法调用都会创建栈帧,存储局部变量、操作数栈、动态链接信息、返回地址等内容,方法执行完毕后栈帧出栈,恢复调用者的执行。