《深入理解Java虚拟机-JVM高级特性与最佳实践》学习总结(第八章)

第八章 虚拟机字节码执行引擎

《深入理解Java虚拟机-JVM高级特性与最佳实践》学习总结(第八章)_第1张图片
虚拟机字节码执行引擎
8.1 运行时栈帧结构

1.什么是栈帧?
栈帧(Stack Frame)是用于支持虚拟机进行方法调用和执行的数据结构,它是虚拟机运行时数据区中的虚拟机栈(Virtual Machine Stack)。
2.栈帧的结构?
栈帧存储了方法的局部变量表、操作数栈、动态连接和方法返回地址等信息。在编译程序代码的时候,栈帧中需要多大的局部变量表、多深的操作数栈都已经完全确定了,并且写入到了方法表的Code属性之中,因此一个栈帧需要分配多少内存,不会受到程序运行期变量数据的影响,而仅仅取决于具体的虚拟机实现。
** 以下是栈帧的逻辑模型 **


《深入理解Java虚拟机-JVM高级特性与最佳实践》学习总结(第八章)_第2张图片
栈帧结构

3.它是如何被操作的?
每一个方法从调用开始到执行完成的过程,就对应着一个栈帧在虚拟机里面从入栈到出
栈的过程。(栈顶的栈帧是有效的,被称为当前栈帧(Current Stack Frame))
4.数据共享特性
如下图:


《深入理解Java虚拟机-JVM高级特性与最佳实践》学习总结(第八章)_第3张图片
两个栈帧之间的数据共享
8.1.1 局部变量表

1.什么局部变量表?
其是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。
2.容量
其是以变量槽(Variable Slot)为最小单位。
** 变量槽是什么? **
虚拟机规范中并没有明确指明一个Slot应占用的内存空间大小,只是很有“导向性”地说明每个Slot都应该能存放一个boolean、byte、char、short、int、float、reference、returnAddress类型的数据,允许Slot的长度随着处理器、操作系统或虚拟机的不同而发生变化。

8.1.2 操作数栈

1.什么是操作数栈?
操作数栈也常被称为操作栈,它是一个先进后出栈。
2.结构?
操作数栈的每一个元素可以是任意的JAVA数据类型,包括long和double,32位数据类型所占的栈容量为1,64位为2。同局部变量表一样,其最大深度也在编译的时候被写入到Code属性的max_stacks数据项之中。
3.调用过程?
当一个方法刚刚开始执行的时候,这个方法的操作数栈是空的,在方法的执行过程中,会有各种字节码指令向操作数栈中写入和提取内容,也就是入栈出栈操作。

8.1.3 动态连接

1.什么是动态连接?
每个栈帧都包含着一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接。通过第六章的讲解,我们知道Class文件的常量池中存有大量的符号引用,字节码中的方法调用指令就以常量池中指向方法的符号引用为参数。这些符号引用一部分会在类加载阶段或第一次使用的时候转化为直接引用,这种转化称为静态解析。另外一部分将在每一次的运行期间转化为直接引用,这部分称为动态连接。

8.1.4 方法返回地址

一般来说,方法正常退出的时候,调用者的PC计数器的值就可以作为返回地址,栈帧中很可能会保存这个计数器值,而方法异常退出的时候,返回地址是要通过异常处理器来确定的,栈帧中一般不会保存这部分信息。

8.2 方法调用

方法调用并不等同于方法执行,该阶段的唯一任务就是确定被调用方法的版本(即调用的是哪一个方法),暂时还不涉及方法内部的具体运行过程。

8.2.1 解析

1.什么是解析?
方法在程序真正运行之前就有一个可确定的调用版本,并且这个方法的调用版本在运行期是不可改变的,这类方法的调用被称为解析。解析调用一定是个静态的过程,在编译期间就完成确定,在类装载的解析阶段就会把涉及的符号引用全部转变为可确定的直接引用,不会延迟到运行期再去完成。
2.适合进行解析的方法类型?
静态方法和私有方法

** JAVA虚拟机里面提供了四条方法调用字节码指令 **
1.invokestatic:调用静态方法
2.invokespecial:调用实例构造器方法,私有方法和父类方法
3.invokevirtual:调用所有的虚方法
4.invokeinterface:调用接口方法,会在运行时再确定一个实现此接口的对象。
只要能被invokestatic和invokespecial指令调用的方法,都可以在解析阶段确定唯一的调用版本,符合这个条件的有静态方法、私有方法、实例构造器和父类方法四类,它们在类加载的时候就会把符号引用解析为该方法的直接引用。这些方法称为非虚方法,与之相反,其他方法就被称为虚方法(除去final方法)

8.2.2. 分派

1.分派(Dispatch)调用则可能是静态的也可能是动态的。
2.分类:
静态单分派、静态多分派、动态单分派、动态多分派
3.静态分派:
所有依赖静态类型来定位方法执行方法的分派动作,都成为静态分派。静态分派的最典型应用就是方法重载。
** 什么是静态类型?**
例如: Human man = new Man()
Human称为变量的静态类型(Static Type)或者外观类型(Apparent Type)
后面的"Man"则称为变量的实际类型(Actual Type)
4.动态分派
在运行期根据实际类型确定方法执行版本的分派过程称为动态分派。
5.单分派与多分派
宗量:方法的接收者与方法的参数统称为方法的宗量
根据分派基于多少种宗量,可以将分派划分为单分派和多分派两种。
单分派是根据一个宗量对目标方法进行选择。多分派则是根据多于一个的宗量对目标方法进行选择。
今天的JAVA语言是一门静态多分派、动态单分派的语言。

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

8.3.1 解释执行
Java语言经常被人们定位为"解释执行"的语言,在JAVA初生的JDK1.0时代,这种定义还算是比较准确的,但当主流的虚拟机中都包含了即时编译器后,Class文件中的代码到底会被解释执行还是编译执行,就成了只有虚拟机自己才能准确判断的事。所以,只有确定了谈论对象是某种具体的Java实现版本和执行引擎运行模式时,谈解释执行还是编译执行才会比较确切。

8.3.2 基于栈的指令集与基于寄存器的指令集
Java编译器输出的指令流,基本上是一种基于栈的指令集架构,指令流里面的指令大部分都是零地址指令,他们依赖操作数栈进行工作。

你可能感兴趣的:(《深入理解Java虚拟机-JVM高级特性与最佳实践》学习总结(第八章))