栈帧是虚拟机运行时数据区中的虚拟机栈的栈元素,储存了局部变量表、操作数栈、动态链接和方法返回地址等栈元素。每一个方法从开始至执行完成的过程,都对应着一个栈帧在虚拟机栈里面从入栈到出栈的过程。执行引擎的所有字节码指令都只针对当前栈帧进行操作。
局部变量表
(1)局部变量表(Local Variable Table)是一组变量储存空间,用于存放方法参数和方法内部定义的局部变量。
(2)容量以变量槽为单位(Variable Slot),可以存放32位以内的数据类型,64位数据需要两个连续的Slot空间。
(4)局部变量表中第0位索引的Slot是用于传递方法所属对象实例的引用(this),接着是参数和内部变量。
(5)Slot是可以重用的,如果PC计数器的值已经超过了某个变量的作用域,那这个Slot可以交给其他变量使用。
(6)GC对于局部变量的回收,取决于局部变量表中的Slot是否还存有该对象的引用。因此对于一些大内存的对象,使用完成后手动设置为null不见得是一个毫无意义的操作。
(7)类变量有两次赋值初始化的过程,一次是准备阶段,一次是初始化阶段。而局部变量是没有准备阶段的,因此局部变量必须初始化。
操作数栈
(1)操作数栈的数据类型可以是任意的Java数据类型,32位所占栈容量1,64位占2,栈深最大值由code属性确定。
(2)当一个方法开始执行时,操作数栈为空。在方法的执行过程中,会有各种字节码指令往栈中写入和提取内容,例如,在做算术运算时就是通过操作数栈执行的。
(3)在概念模型中,栈帧作为虚拟机栈的元素,是完全相互独立的。但在大多数虚拟机的实现里都会做一些优化处理,让下面栈帧的部分操作数栈和上面栈帧的部分局部变量表重叠,这样在进行方法调用时可以共用一部分数据。
动态链接
每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态链接。Class文件中的符号引用一部分在类加载阶段进行静态解析转为直接引用,另一部分在每一次运行期间转为动态引用,称为动态连接。
方法返回地址
(1)当一个方法开始执行时,只有两种方式可以退出。第一种方式是执行引擎遇到任意一个方法返回的字节码指令,这时候可能会有返回值传递给上层方法的调用者,是否有返回值和返回值类型由返回指令决定,这种退出称为正常完成出口。
(2)另一种是遇到异常,并且没有在方法体内得到处理,称为异常完成出口。一个方法使用异常完成出口的方式退出,是不会产生任何返回值 的。
(3)无论采用何种方式退出,都需要返回方法被调用的位置。一般来说,方法正常退出时,调用者的PC计数器的值可以作为返回地址,栈帧中很可能会保留这个计数器值。而异常退出时,返回地址需要使用异常表确定。
(4)方法退出的过程实际上就等同于把当前栈帧出栈,因此退出时可能执行的操作有:回复上层方法的局部变量表和操作数栈,把返回值压入调用者栈帧的操作数栈中,调整PC计数器的值以指向方法调用指令后面的一条指令。