一、执行引擎是什么?
执行引擎是Java虚拟机最核心的组成部分之一。对于物理机来说执行引擎是建立在处理器、硬件、指令集和操作系统层面上的,而虚拟机的执行引擎是自己实现的,他可以自行定义指令集和执行引擎的结构体系,并且执行那些不能够被硬件直接支持的指令集格式。
所有的Java虚拟机的执行引擎都是一致的:输入的是字节码文件,处理过程是字节码解析的等效过程,输出的是执行结果。
二、什么是栈帧?
栈帧是用于支持虚拟机进行方法调用和方法执行的数据结构,是虚拟机运行时数据区中的虚拟机栈的栈元素。
一个方法从调用开始到执行完成都对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
活动线程中只有栈顶的元素是有效的,称为当前栈帧。这个栈帧所关联的方法称为当前方法。执行引擎所运行的所有字节码指令都只针对当前栈帧进行操作。
栈帧的结构?
1、局部变量表
最大容量:在Class文件的Code属性的max_locals数据项中确定了该方法所需要分配的最大局部变量表的容量。
单位:变量槽(Slot),虚拟机规范字没有明确指出一个Slot的大小,但是一个Slot可以存放一个32位以内的数据类型。具体的大小会根据处理器、操作系统和虚拟机的不同而不一样。
32位以内的数据类型:boolean、byte、char、short、int、float、reference(对对象的引用)、returnAdress(指向一条字节码指令的地址)。
虚拟机怎么使用局部变量表?
使用索引定位的方式。索引范围从0开始,如果是32位数据类型,索引n就是代表使用了第n个Slot;如果是64位数据类型,说明使用n和n+1两个Slot。
2、操作数栈
最大深度:在Class文件的Code属性的max_stacks数据项中定义了。
操作数栈里面放的什么?
任意的Java数据类型,32位数据类型占栈容量为64位数据类型占栈容量为2.
3、动态链接、静态解析
常量池中指向方法的符号引用在类加载阶段或第一次使用的时候转化为直接引用,这种转化称为静态解析。
常量池中指向方法的符号引用在每一次的运行期间转化为直接引用,这种转化称为动态链接。
4、方法返回地址
一个方法被执行后分为两种退出方式:(1)正常完成出口:会将返回值传给上层的方法调用者。调用者的PC计数器的值可以作为返回的地址,保存在栈帧当中。
(2)异常完成出口:返回地址需要通过异常表来确定,栈帧中不会保存这部分信息。
5、附加信息
在栈帧中增加的虚拟机规范里没有描述的信息。
三、方法调用
方法调用
方法调用阶段的唯一任务就是确定被调用方法的版本。
方法内部的具体运行过程是方法执行。
1、解析调用——静态
方法在程序真正运行之前(程序代码写好、编译器进行编译之前)就有一个可确定的调用版本,并且这个方法的调用版本在运行期是不可改变的。这类方法的调用称为解析。
可以在解析阶段确定唯一调用的版本有静态方法、私有方法、实例构造器和父类方法四种(也称非虚方法,final方法也是一种非虚方法)。他们在类加载的时候就会把符号引用解析为该方法的直接引用。
2、分派
静态分派:所有依赖静态类型来定位方法执行版本的分派动作,例如方法重载。静态分派发生在编译阶段,不是虚拟机执行的。
动态分派:在运行期根据实际类型确定方法执行版本的分派过程,例如方法重写。
单分派:根据一个宗量对目标方法进行选择。(宗量是方法的接收者和方法参数的统称)
多分派:根据多个宗量对目标方法进行选择。
3、JVM提供了4条方法调用的字节码指令:
- invokestatic:调用静态方法
- invokespecial:调用实例构造器<init>方法,私有方法和父类方法
- invokevirtual:调用所有的虚方法
- invokeinterface:调用接口方法,会在运行时再确定一个实现此接口的对象。
四、方法执行(基于栈的字节码解释执行引擎)
javac编译器完成了程序代码经过词法分析、语法分析到抽象树,再遍历语法树生成线性的字节码指令流的过程。
…………