栈帧(Stack Frame)是用于支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区中的虚拟机栈的栈元素。每一个栈帧都包括了局部变量表、操作数栈、动态连接、方法返回地址和一些额外的附加信息。在编译程序代码的时候,栈帧中需要多大的局部变量表、多深的操作数栈都已经完全确定了,并且写入到方法表的 Code 属性之中。
局部变量表
局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。方法 Code 属性的 max_locals 数据项确定了该方法所需要分配的最大局部变量表的容量。
局部变量不存在“准备”阶段,如果一个局部变量定义了但没有赋初始值是没法使用的。
赋null 值的操作在经过虚拟机 JIT 编译器优化之后会被消除掉。
操作数栈
操作数栈是一个后入先出(LIFO)栈,当一个方法刚刚开始执行的时候,这个方法的操作数栈是空的,在方法的执行过程中,会有各种字节码指令向操作数栈中写入和提取内容,也就是入栈和出栈操作。方法 Code 属性的 max_stacks 数据项设定了操作数栈的最大深度。
动态连接
Class 文件常量池中指向方法的符号引用中会有一部分在运行期间转化为直接引用,这部分称为动态连接。
与动态连接相对应的是静态连接。静态方法、私有方法、实例构造器、父类方法和 final 方法统称为虚方法,虚方法的调用没有其他版本,无须对方法接收者进行多态选择,因此它们在类加载的解析阶段就会把涉及到的符号引用全部转变为可确定的直接引用,不会延迟到运行期才去完成。而有些方法,比如重载和重写方法,具有多个版本,无法直接确定调用的是什么版本,这部分符号引用的转换就必须等到运行期来完成。
方法返回地址
方法返回地址指的是方法退出后的返回地址。一般来说,方法正常退出时,调用者的 PC 计数器的值就可以作为返回地址,栈帧中很可能会保存这个计数器值。而方法异常退出时,返回地址是要通过异常处理器来确定的,栈帧中一般不会保存这部分信息。
附加信息
附加信息指的是在虚拟机实现中加入了一些规范里没有描述的信息到栈帧之中,例如与调试相关的信息。