执行引擎概念模型
所有虚拟机的执行引擎是一致的:输入是字节码文件,处理过程是字节码解析的等效过程,输出的是执行结果。
栈帧是用于支持虚拟机进行方法调用和方法执行的数据结构, 它是虚拟机运行时数据区中的虚拟机栈的栈元素。栈帧存储了方法的局部变量表,操作数栈,动态连接和方法返回地址等信息。每一个方法从调用开始到执行完成的过程,都对应着一个栈帧在虚拟机栈从入栈到出栈的过程。
局部变量表
是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。在Java程序编译为Class文件时,就在方法的Code属性的max_locals数据项中确定了该方法所需要分配的局部变量表的最大容量。
==局部变量表的容量以变量槽slot为最小单位,一个slot可以存放一个32位以内的数据类型,java中占用32以内的数据类型有boolean,byte,char,short,int,float,reference和returnAddress8种类型。对于64位的数据类型,虚拟机以高位对齐的方式为其分配两个连续的Slot空间。
虚拟机通过索引定位的方式使用局部变量表,索引值的范围是从0开始至局部变量表最大的Slot数量。
操作数栈
后入先出栈。操作数栈的最大深度也在编译的时候写入到Code属性的max_stacks数据项中。32位数据类型所占的栈容量为1,64位数据类型所占的栈容量为2。
操作数栈中元素的数据类型必须与字节码指令的序列严格匹配,在编译程序代码的时候,编译器要严格个保证这一点,在类校验阶段的数据流分析还要再次验证这一点。
动态连接
每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接。符号引用中一部分将在每一次运行期间转化为直接引用,这部分称为动态连接。
方法返回地址
可分为正常完成出口和异常完成出口
正常完成出口:执行引擎遇到任意一个方法返回的字节码指令,这时候可能会有返回值传递给上层的方法调用者,是否有返回值和返回值的类型将根据遇到何种方法返回指令来决定,这种退出方法的方式称正常完成出口。
异常完成出口:在方法执行过程中遇到了异常,并且这个异常没有在方法体内得到处理,无论是Java虚拟机内部产生的异常,还是代码中使用throw字节码指令产生的异常,只要在本方法的异常表中没有搜素到匹配的异常处理器,就会导致方法退出,这种退出方法的方式称为异常完成出口。
方法退出的过程实际就等同于吧当前栈帧出栈,因此退出时可能有的操作:恢复上层方法的局部变量表和操作数栈,把返回值亚茹调用者栈帧的操作数栈中,调整PC计数器的值以指向方法调用指令后面的一条指令等。
基于栈的字节码解释执行引擎
java虚拟机的执行引擎在执行java代码的时候都有解释执行和编译执行两种选择
C/C++语言:词法分析,语法分析以至后面的优化器和木比偶代码生成器都可以选择独立于执行引擎,形成一个完整意义的编译器去实现。
Java语言:把2其中一部分步骤(如生成抽象语法树之前的步骤)实现为一个半独立的编译器。
JavaScript执行器:把这些步骤和执行引擎全部集中封装在一个封闭的黑匣子之中。
java语言中javac编译器完成了程序代码经过词法分析,语法分析到抽象语法树,在遍历语法树生成线性的字节码指令流的过程,因为这一部分是在javau虚拟机之外进行的,而解释器在虚拟机的内部,所以java程序的编译就是半独立的实现。