执行引擎是 Java 虚拟机核心的组成部分之一,执行引擎由软件自行实现
目录
- 一、运行时栈帧结构
- 二、方法调用
- 三、基于栈的字节码解释执行引擎
- 四、OSGI:灵活的类加载器架构
一、运行时栈帧结构
- Java 虚拟机以方法作为最基本的执行单元,“栈帧”则是用于支持虚拟机进行方法调用和方法执行背后的数据结构
- 栈帧包括局部变量表、操作数栈、动态链接、方法返回地址和一些额外的附加信息
- 在编译 Java 源码的时候,需要多大的局部变量表,多深的操作数栈已经被计算出来了,并写入了 Code 属性之中
- 在活动线程中,只有位于栈顶顶方法才是在运行的(当前栈帧)
- 局部变量表
- 一组变量值的存储空间,存放方法参数和方法内部定义的局部变量,容量以变量槽为最小单位
- 每个变量槽应占用 32 位长度的内存空间
- 可以随着处理器、操作系统或虚拟机实现的不同而发生变化
- 需要用对齐和补白的手段让变量槽在外观上看起来与 32 位虚拟机中一致
- Java 虚拟机会以高位对齐的方式为其分配两个连续的变量槽空间(long、double)
- 操作数栈
- 操作栈,后入先出,最大深度在编译的时候被写入到 Code 属性的max_stacks 数据项中
- Javac 编译器的数据流分析工作保证了在方法执行的任何时候,操作数栈道深度都不会超过在 max_stacks 数据项中设定的最大值
- 刚刚开始执行时,各种字节码指令往操作数栈中写入和提取内容,也就是出栈入栈
- 两个不同的栈帧作为不同方法的虚拟机栈元素,是相互独立的
- Java 虚拟机的解释执行引擎被称为“基于栈道执行引擎”,栈就是操作数栈
- 动态连接
- 每个栈帧都包含一个指向运行时常量池中该栈帧所属方法都引用,这个引用是为了支持方法调用过程中的动态连接
- 符号引用一部分会在类加载阶段或者第一次使用的时候就被转化为直接引用,这就是静态解析
- 每一次运行期间都转化为直接引用,这部分就称为动态连接
- 方法返回地址
- 方法退出方式
- 执行引擎遇到任意一个方法返回的字节码指令(正常调用完成)
- 正常退出时,主调方法的 PC 计数器的值可以作为返回地址,栈帧中很可能会保存这个计数器值
- 方法执行过程中遇到异常(异常调用完成)
- 异常退出时,通过异常处理器表来确定的
- 执行引擎遇到任意一个方法返回的字节码指令(正常调用完成)
- 退出时可能的操作
- 恢复上层方法的局部变量表和操作数栈,把返回值压入调用者栈帧的操作数栈中,调整 PC 计数器的值以指向方法调用指令后面的一条指令等
- 方法退出方式
二、方法调用
- 解析
- 所有方法调用的目标方法在 Class 文件里面都是一个常量池中的符号引用
- 在类加载的解析阶段,会将其中的一部分符号引用转化为直接引用
- 方法在程序真正运行之前就有一个可确定的调用版本,并且这个方法的调用版本在运行期是不可改变了
- 分派
- 静态分派
- 重载
- 动态分派
- 重写
- 单分派和多分派
- 静态分派
- 动态类型语言
- 关键特征就是它的类型检查的主体过程是在运行期而不是编译期进行的
三、基于栈的字节码解释执行引擎
- 解释执行
- 代码执行过程
- javac 编译器完成了程序代码经过词法分析、语法分析道抽象语法树,再遍历语法树生成线性的字节码指令流的过程
- 这一部分动作在 Java 虚拟机之外进行,而解释器在虚拟机内部,所以Java 程序的编译就是半独立的实现
- 代码执行过程
- 基于栈的指令集与基于寄存器的指令集
- javac 编译器输出的字节码指令流,基本上是一种基于栈的指令集架构,字节码指令流里面的指令大部分都是零地址指令,他们依赖操作数栈工作
- 基于栈的解释器执行过程
四、OSGI:灵活的类加载器架构
- 基于 Java 语言的动态模块化规范
- 在 OSGI 里面,Bundle 之间的依赖关系从传统的上层模块依赖底层模块转变为平级模块之间的依赖,而且类库的可见性能得到非常精确的控制
- 一个模块只有被 Export 过的 Package 才可能被外界访问,其他的 Package 和 Class 将会被隐藏起来
- OSGI 的 Bundle 类加载器之间只有规则,没有固定的委派关系
- Bundle 类加载器为其他 Bundle 提供服务时,会根据 Export-Package 列表严格控制访问范围
- 如果没有 Export,不会提供给其他 Bundle 使用,也不会把其他 Bundle 的类加载请求分配给这个 Bundle 来处理