第八章:虚拟机字节码执行引擎
运行时栈帧结构
栈帧是用于支持虚拟机进行方法调用和方法执行的数据结构。它是虚拟机栈中的栈元素。栈帧中存储了方法的局部变量表,操作数栈,动态链接和方法返回地址等信息。每一个方法调用从开始至执行完成的过程,都对应着一个栈帧在虚拟机栈里面从入栈到出战的过程。
接下来讲解栈帧中的局部变量表,操作数栈,动态链接,方法返回地址等各个部分的作用和数据结构
局部变量表
是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。在编译时,就在方法的Code属性的max_locals数据项中确认了该方法所需要分配的局部变量表的最大容量。
操作数栈
他是一个后入先出站。同局部变量表一样,操作数栈的最大深度也在编译时写入到Code属性的max_stacks数据项中。栈中的每一个元素可以是java的任意类型,32位的栈容量为1,64位的栈容量为2。
Java虚拟机的解释执行引擎成为“基于栈的执行引擎”,其中所指的“栈”就是操作数栈。
动态连接
每个栈帧都包含一个指向运行时常量池中该栈所述方法的引用,持有这个引用的是为了支持方法调用过程中的动态连接。
方法返回地址
….
方法调用
方法调用不等于方法执行,方法调用阶段是确定要用哪一个方法。Class文件编译过程中不包含传统编译的连接步骤,一切方法调用在Class文件里面只是符号引用,而不是方法在实际运行是内存布局的入口地址(直接引用)。这个特性给Java带来了强大的动态扩展能力。在类加载期间,甚至运行期间才能确定目标方法的直接引用。
解析
所有方法调用的目标方法在Class文件中都是常量池中的符号引用,在类加载的解析阶段,会将其中一部分转换成直接引用。前提是:方法在程序真正运行前就已经确定了。编译期可知,运行期不可变。
符合条件的方法是:静态方法和私有方法,实例构造器,父类方法。
分派
分派调用过程会揭示多条性特征的最基本体现,包括重载,重写在Java中如何实现的。
- 静态分派
虚拟机在重载时是通过参数的静态类型而不是实际类型作为判定依据的。并且静态类型是编译器可知的。
所有依赖静态类型来定位方法执行版本的分派动作称为静态分派;典型应用就是方法重载。
- 动态分派
典型的应用是重写,我们把在运行期根据实际类型确定方法执行版本的分派过程称为动态分派
- 单分派与多分派
单分派是根据一个宗量对目标方法进行选择,多分派是根据多余一个宗量对目标方法进行选择。
Java是一门静态多分派,动态单分派的语言。
在JDK1.7中实现的JSR—-292开始提供对动态语言的支持,新增了invokedynamic指令。
动态类型语言支持
随着JDK7的发布,字节码指令集迎来新成员-invokedynamic指令。该指令就是为实现动态类型语言支持而进行的改进之一。
动态类型语言
动态类型语言的关键特征就是它的类型检查的主题过程是在运行期不是编译期(Groovy JavaScript)。相对的,编译器就进行类型检查的语言是静态类型语言(C++ JAVA)
基于栈的字节码解释执行引擎
许多Java虚拟机的执行引擎在执行Java代码的时候都有解释执行和编译执行两种选择。
解释执行
…