Java虚拟机以方法作为最基本的执行单元,栈帧是虚拟机栈的栈元素
每一个栈帧都包括了局部变量表、操作数栈、动态连接、方法返回地址和一些额外的附加信息。
同一时刻同一条线程中,只有位于栈顶的栈帧才是在运行的,叫做当前栈帧,其关联的方法称为当前方法
局部变量表
局部变量表用于存放方法参数和方法内定义的局部变量
栈帧中的局部变量表的大小(slot)在编译阶段就已经确定了,运行期不会被改变,并且大小写入在方法表的code属性中。
关于基本单位slot
- 变量槽variable slot为局部变量表的最小单位,一般按照32位的物理内存来存储,因此有填充对齐的手段
一个slot中可以存放:boolean,byte,char,short,int,float,reference,returnAddress(现在很少见了)
其中reference一般用来做两种事情
- 指向Java堆中对象的起始地址或索引
- 指向对象所属数据类型在方法区中的类信息
- 对于64位的数据(long, double)使用两个slot,并采用高位补齐的方式填充,读取也是分两次读,不允许任何情况的单独访问
slot的索引安排
- 一个slot对应一个索引坐标,从0开始
- 第0号索引存放的是方法所属对象实例的引用(this)
- 之后按照方法参数表的顺序进行排列其余参数
- 最后分配方法体内定义的变量顺序分配局部变量
slot的复用
如果当前位置已经超过某个变量的作用域时,例如出了定义这个变量的代码块,这个变量对应的 Slot 就可以给其他变量使用了。但同时也说明,只要其他变量没有使用这部分 Slot 区域,这个变量就还保存在那里,这会对 GC 操作产生影响。
操作数栈
- 操作数栈为一个后入先出的标准栈结构,32位数据类型占1个栈容量,64位占2个
- 方法的执行过程中会有各种字节码指令往操作数栈中写入和提取内容(类似于寄存器读取操作写入)
- 操作数栈中的元素数据类型必须和字节码指令的类型严格匹配(编译器+类校验双重保证)
大多数虚拟机里面都会让两个栈帧进行一部分重叠
- 下面的操作数栈的一部分和上面的局部变量表的一部分重叠
动态连接
- 指向方法区的运行时常量池中该栈帧所属方法的引用
- 为了支持方法调用中的动态连接
方法区返回地址
当一个方法执行后,只有两种方式退出这个方法
执行方法返回的字节码指令
- 此时可能有返回值传递给上层方法调用者
- 这种退出方式成为正常调用完成
遇到了异常
- 没有返回值
- 叫做异常调用完成
无论是哪种方式退出,都必须要保存一些信息来恢复上层方法的执行状态,一般的操作有
- 恢复上层方法的局部变量表和操作数栈
- 把返回值(if exist)压入上层方法的操作数栈中
- 调整PC的值
动态连接、方法返回地址和其他附加信息有时也会被归类成为栈帧信息