JVM的栈里面存的是栈帧,栈帧里面存的是什么?

发布于:2025-02-12 ⋅ 阅读:(35) ⋅ 点赞:(0)

JVM 栈帧(Stack Frame)—— 更完整的解释

栈帧(Stack Frame)是 JVM 方法执行时的 最小单位,每个方法被调用时,JVM 都会在**虚拟机栈(JVM Stack)**中创建一个栈帧。

当方法执行完成后,栈帧出栈,方法调用结束。

📌 一个栈帧的结构(完整)

栈帧组件 作用
局部变量表(Local Variable Table) 存放方法的参数局部变量(基本类型、对象引用、returnAddress)
操作数栈(Operand Stack) 执行方法的操作数 存取,JVM 指令从这里取数、计算、存结果
动态链接(Dynamic Linking) 维护常量池 中的符号引用到实际方法、字段的解析(指向方法区中的方法)
返回地址(Return Address) 记录当前方法调用者的地址(方法返回时,继续执行调用者的指令)
附加信息(Additional Info) 存放异常处理表、JIT 编译优化信息等

📌 更详细的解析

1. 局部变量表(Local Variable Table)

  • 作用:存放方法中的 参数局部变量,按索引访问。
  • 存储内容
    • 基本数据类型(int、long、float、double、byte、short、char、boolean)。
    • 对象引用(指向堆中的对象)。
    • returnAddress(用于 jsr/ret 指令,主要用于 finally 处理)。
  • 索引规则
    • 参数 按顺序放入索引 0 开始的槽位。
    • static 方法的 第 0 号槽位是 this
    • longdouble 类型占两个槽位(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)

2. 操作数栈(Operand Stack)

  • 作用:执行方法的 中间计算(比如加法、对象调用方法)。
  • 执行方式
    • JVM 是基于栈的解释器,指令从 操作数栈 取数 → 计算 → 结果入栈。
    • 每个方法的操作数栈大小在编译期就确定(字节码 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

3. 动态链接(Dynamic Linking)

  • 作用:在运行时 解析 常量池中的方法/字段符号引用,指向实际方法/字段
  • 两种链接方式
    • 静态解析(Static Resolution):编译时确定的方法调用(final 方法、private 方法、static 方法)。
    • 动态链接(Dynamic Linking):运行时动态绑定,比如 多态方法调用

示例:多态方法调用

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()" 方法的符号引用

执行过程:

  1. 动态链接解析,找到 Child 类的 say() 方法(因为 obj 实际是 Child)。
  2. 方法调用

4. 返回地址(Return Address)

  • 作用:方法执行完后,返回调用者的下一条指令(方法调用后要恢复原来的执行流)。
  • 不同情况
    • 正常返回(直接回到调用者的下一条指令)。
    • 异常返回(查找异常处理表,异常未捕获则终止)。

示例

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 运行时调用方法的基本单位,每个方法调用都会创建栈帧,存储局部变量、操作数栈、动态链接信息、返回地址等内容,方法执行完毕后栈帧出栈,恢复调用者的执行。