深入理解java虚拟机----第八章虚拟机字节码执行引擎

8.1 概述

执行引擎:输入字节码文件,处理过程是字节码解析的等效过程,输出的是执行结果

8.2 运行时栈帧结构

栈帧是支持虚拟机进行方法调用和方法执行的数据结构。它存储在运行时数据区的虚拟机栈中。

每一个方法的从开始到完成的过程,都对应了一个栈帧的入栈和出栈的过程。

一个栈帧包含了:局部变量表,操作数栈,动态连接,方法返回地址。

局部变量表和操作数栈在编译的时候,已经可以完全确定,并且写入到了Class文件的方法表的Code属性之中。因此一个栈帧需要多大的内存,不会受到程序运行期的变量数据影响。

深入理解java虚拟机----第八章虚拟机字节码执行引擎_第1张图片

 

8.2.1 局部变量表

用来存放方法参数和方法中的局部变量。

如果是占用内存比较大的对象,在使用结束但是作用域还有其他执行比较长的语句之前,可以把它置为null,然后就可以被gc。但是不建议对所有的对象都这个处理,没有必要的地方不需要有这么多的类似代码。

同时,使用JIT编译执行的时候,赋null值的操作将会被抹除。

局部变量不会被赋予初始值,所以必须初始化才能使用。

 

8.2.2 操作数栈

一个后进先出的栈。

我理解的,局部变量用于存储,操作数用于计算。比如执行一个加法操作,需要将两个数值压入操作数栈顶,调用其他方法的时候,可以通过操作数栈来传递参数。

 

8.2.3 动态链接

每个方法都包含一个指向运行时常量池中的方法引用。持有这个引用(这里应该是指符号引用)就可以支持在方法调用的过程中动态连接。

 

8.2.4 方法返回地址

方法只有2中退出方式,正常情况下,遇到return指令退出。还有就是异常退出。

正常情况:一般情况下,栈帧会保存 在程序计数器中的调用者的地址。虚拟机通过这个方式,执行方法调用者的地址,

然后把返回值压入调用者中的操作数栈。

异常情况:方法不会返回任何值,返回地址有异常表来确定,栈帧一般不存储信息。

8.2.5 附加信息

8.3 方法调用

方法调用不等同于方法执行,唯一任务就是确定被调用方法的版本

8.3.1 方法调用

方法调用阶段不是执行该方法,而仅仅时确认要调用那个方法。class文件在编译阶段没有连接这一过程,、

所以动态连接这个在C++就已经有的技术,在java运用到了一个新的高度。所有的函数(除了私有方法,构造方法 & 静态方法,下同),理论上

都可以时C++里面的虚函数。所以所有的函数都需要通过动态绑定来确定“明确”的函数实体。

解析

所有方法调用的目标方法都是常量池中的符号引用。在类的加载解析阶段,会将一部分目标方法转化为直接引用。(可以理解为具体方法的直接地址)

可以转化的方法,主要为静态方法 & 私有方法。

Java虚拟机提供5中方法调用命令:

invokestatic:调用静态方法

invokespecial:调用构造器,私有方法和父类方法

invokevirtual:调用虚方法

invokeinterface:调用接口方法

invokedynamic:现在运行时动态解析出该方法,然后执行。

invokestatic & invokespecial 对应的方法,都是在加载解析后,可以直接确定的。所以这些方法为非虚方法。

java规定 final修饰的是一种非虚方法。

8.3.2 分派

静态分派

静态类型(外观类型):变量本身的静态类型不会被改变,最终的静态类型是在编译期可知的

实际类型:变化结果在运行期才确定

使用哪个版本的重载,完全取决于传入参数的数量和数据类型;虚拟机(准确的说是编译器)在重载时是通过参数的静态类型而不是实际类型作为判定依据;并且静态类型是编译期可知的,因此,在编译阶段,javac编译器会根据参数的静态类型决定使用哪个版本的重载

所有依赖静态类型来定位方法执行版本的分派称为静态分派

静态分派的典型应用就是方法重载

动态分派

重写

invokevirtual的运行时解析过程:

找到操作数栈顶的第一个元素指向的对象的实际类型,记作C

如果类型C中找到与常量中的描述符和简单名称都相符的方法,则进行访问权限校验,如果通过则返回这个方法的直接引用,查找过程结束,如果不存在,则返回java.lang.IllegalAccessError异常

否则,按照继承关系从下往上依次对C的各个父类进行第二步的查搜索和验证过程

如果始终没有找到,则抛出java.lang.AbstractMethodError异常

运行期根据实际类型确定方法版本的分派称为动态分派

单分派和多分派

方法的接受者方法的参数统称为方法的宗量,根据分派基于多少种宗量,可以将分派划分为单分派和多分派

单分派是根据一个宗量对目标方法进行选择

多分派是根据多于一个宗量对目标方法进行分派

静态分派属于多分派

动态分派属于单分派

虚拟机动态分派的实现

动态类型语言支持

8.4 基于栈的字节码解释执行引擎

8.4.1解释执行

javac编译器完成了程序代码经过词法分析、语法分析到抽象语法树,在遍历语法树生成线性的字节码指令流的过程

一部分在虚拟机之外进行,而解释器是在虚拟机内部,所以Java程序的编译是半独立的实现

8.4.2基于栈的指令集和基于寄存器的指令集

基于栈的指令集主要优点是可移植性、代码更加紧凑、编译实现更简单;缺点是执行速度相对慢点

8.4.3基于栈的解释器执行过程

深入理解java虚拟机----第八章虚拟机字节码执行引擎_第2张图片

深入理解java虚拟机----第八章虚拟机字节码执行引擎_第3张图片

深入理解java虚拟机----第八章虚拟机字节码执行引擎_第4张图片

深入理解java虚拟机----第八章虚拟机字节码执行引擎_第5张图片

深入理解java虚拟机----第八章虚拟机字节码执行引擎_第6张图片

深入理解java虚拟机----第八章虚拟机字节码执行引擎_第7张图片

深入理解java虚拟机----第八章虚拟机字节码执行引擎_第8张图片

你可能感兴趣的:(jvm)