虚拟机字节码执行引擎——运行时栈贞结构

      所有Java虚拟机的执行引擎都是一致的:输入的事字节码文件,处理过程是字节码解析的等效过程,输出的事执行结果。

      栈帧(Stack Frame)是用于 支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区中的虚拟机栈的栈元素,栈贞存储了方法的局部变量表、操作数栈、动态链接和方法返回地址等信息,每一个方法从调用开始至调用完成的过程都对应着一个栈贞(Stack Frame)从虚拟机栈从入栈到出栈的过程。

      在编译程序的代码的时候,栈帧中需要多大的局部变量表、多深的操作数栈都已经完全确定了,并且写入到方法表的code属性之中,因此一个栈贞需要分配多少内存,不会受到程序运行期间变量数据的影响,而仅仅取决于虚拟机的实现。


栈贞概念结构

      一个线程中的方法调用链可能很长,很多方法可能同时都处于执行状态,对于执行引擎来说,在活动线程中,只有位于栈顶的栈贞才有效,称为当前栈帧(Current Stack Frame),与这个栈帧相关联的方法称为当前方法,执行引擎的所有字节码都只针对当前栈帧进行操作,栈帧的概念结构如上图所示。

1.局部变量表

       局部变量表(Local Variable Table)是一组变量值的存储空间,用于存放方法参数和方法内部定义的局部变量。在Java程序被编译为Class文件时,就在方法的code属性中max_locals数据项中确定该方法所需要分配的局部变量表的最大容量。

      局部变量表的容量以变量槽(Slot)为最小单位,虚拟机规范中并没有明确指明一个Slot应占用的内存空间大小,只是很导向性地说到每一个Slot都应该存放一个boolean、byte、char、short、int、float、refrence类型的数据,这几种类型数据都可以用32位或者更小的物理内存来存放,但这种描述 与明确指出“每一个Slot占用32位长度的内存空间”,是有一些差别的,它允许Slot的长度可以随着处理器、操作系统或者 虚拟机的不同而发生变化。

      虚拟机使用索引定位的方式 使用局部变量表,索引的方式从0开始至局部变量表最大的Slot数量。

      在方法执行时,虚拟机使用局部变量表完成参数值到参数变量表的传递过程,如果执行的时实例方法,那局部变量表中第0位索引的Slot默认是用于传递方法所属对象实例的引用,在方法中可以通过“this”来访问这个隐含的参数,其余参数按照参数表顺序排列,参数表分配完毕,在根据方法内部定义的变量顺序和作用域分配其他的Slot。为了节省内存空间,局部变量表的Slot是可以重用的,方法体中定义的变量其作用域不一定覆盖整个方法体,如果当前字节码PC计数器已经超过某个变量的作用域,那这个变量的Slot就可以交给其他变量使用。

2.操作数栈

      操作数栈(Operand Stack)也称为操作栈,它是一个后入先出栈,同局部变量表一样,在代码编译的时候,操作数栈的最大深度写入到了Code属性中max_stacks数据项中,操作数栈中每一个元素可以是 任意的Java数据类型,包括long和double,32数据类型占的栈容量是1,64位数据的栈容量是2,在方法执行过程中操作数栈的深度都不会超过max_stacks数据项中规定的最大值。

     在方法刚开始执行的时候,这个方法的操作数栈是空的,在方法执行的过程中会有各种字节码指令往里面写入和 提取内容,也就是入栈和出栈的过程,方法中调用其他方法的时候是通过操作数栈来进行参数传递的。


两个栈贞之间数据共享

另外,在概念模型中,两个栈帧作为虚拟机栈的元素是完全相对独立的,但在大多虚拟机里面都会做一些优化,令两个栈帧出现一些重叠,共享一些数据。

3.动态连接

当一个方法执行时,只有两种方式可以退出这个方法;

4.方法返回地址

4.1正常完成出口

执行引起遇到任意一个方法返回的字节码指令,这时候可能会有返回值传递给上层的方法调用者,是否有返回值或者返回值的类型根据遇到 何种方法方法返回指令来决定。

4.2 异常完成出口

方法执行过程中遇到异常,并且这种异常在方法体中没有得到处理,无论是虚拟机异常还是代码异常。

无论何种异常,在方法退出之后,都需要返回 方法被调用的位置,程序才能继续 执行,方法返回时需要在栈帧中保存一些信息,用来帮助回复它上层方法的执行状态。方法正常退出时,PC计数器的值可以作为返回地址,方法异常退出时,返回地址是通过异常处理器来确定的,栈帧中一般不会保存这个信息。

5.附加信息

你可能感兴趣的:(虚拟机字节码执行引擎——运行时栈贞结构)