执行引擎:输入的字节码文件,输出的执行结果
运行时栈帧结构
栈帧是用于支持虚拟机进行方法调用和方法执行的数据结构。栈帧存储了方法的局部变量表,操作数栈,动态连接和方法返回地址等信息
前面说到class文件的方法表集合,每一个方法表后面都可以有一个code属性,存放方法体里面的代码翻译过来的字节码指令,code属性的参数有最大操作数栈深度,局部变量表的大小,这些都是在编译期间确定的。那么在执行引擎运行字节码文件时,调用一个方法就会在虚拟机栈生成一个方法对应的栈帧,栈帧是线程私有。对于执行引擎,位于栈顶的栈帧是有效的。那么栈帧的结构是由是什么样的?
局部变量表
局部变量表以一组变量存储空间,存放方法的参数和内部定义的局部变量。变量槽slot是局部变量的最小存储单位。关于slot存储在class文件结构code属性中说明。
JVM通过slot的数量从0开始作为索引使用局部变量表,使用的过程就是完成参数值到参数变量列表的传递过程。参数值在方法表的LocalVariableTable属性里,如下可以看见class文件的LocalVariableTable属性。局部变量表的第0个索引就是方法所属的this对象,其余参数按照参数表顺序排列。
前面说过局部变量表的slot可以根据变量的作用域复用,那么对于垃圾回收会有什么样的影响
place的作用范围只在括号内,那运行到System.gc()按道理place可以被回收但是并没有
当加入代码int a=0;根据slot复用规则,a会存放place之前的变量槽,这个时候在进行gc,发现place被回收
对于局部变量表中的变量,不同于类变量,我们知道类变量有两次赋值操作。一次在准备阶段赋值成变量类型的零值,所以类变量申明完就可以使用。第二次在初始化阶段,赋值为程序中的值。对于局部变量之声明没有赋值是不能使用的。
操作数栈
方法的操作数栈的最大深度在编译完之后就确定了,存储在方法的code属性中,操作数栈是一个后入先出的栈,当一个方法刚刚被调用的时候,这个栈是空的,执行过程中,会有各种字节码指令往操作数栈中写入和提取内容。
比如在执行iadd字节码指令时,会有两个int类型的数据在操作数栈顶,遇到这个iadd,会将两个int数值出栈,并将结果入栈。
一个方法与另一个方法的操作数栈之间会有部分重叠。
动态连接
每一个栈帧中都包含一个指向运行时常量中该栈帧所属方法的引用,这样就可以支持动态连接。
class文件的常量池有大量的符号引用,当执行字节码指令的方法调用指令时,就以常量池中指向方法的符号引用作为参数。这些引用一部分会在类加载阶段或者第一次使用转化为直接引用。还有一部分将在每一次运行期间转化为直接引用,这部分就叫做动态连接
方法返回地址
当一个方法开始执行,有两种方式退出这个方法
第一种是执行引擎遇到返回字节码指令,说明方法正常退出,可能会把返回值传递给上层调用者或者跳转到下一条指令
另一种是在执行过程中遇到异常,并且这个异常没有在程序中得到处理不论是jvm产生的异常还是athrow字节码指令产生的异常,只要是在异常表中没有收到匹配的异常,方法就会非正常退出,这个时候方法是不会给上次调用返回值的
不管何种方式退出方法,都对应这方法栈帧的出栈过程,所以方法退出执行的操作:
回复上层方法的局部变量表和操作数栈,把返回值(有)压入调用者栈帧的操作数栈,调整PC计数器的值执行方法调用执行完的下一条要执行的指令
附加信息
增加一些额外信息到栈帧中
一般把方法返回地址,动态连接和附加信息称为栈帧信息