如需转载,请根据 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 许可,附上本文作者及链接。
本文作者: 执笔成念
作者昵称: zbcn
本文链接: https://1363653611.github.io/zbcn.github.io/2019/12/15/JVM_07%E5%AD%97%E8%8A%82%E7%A0%81%E6%89%A7%E8%A1%8C%E5%BC%95%E6%93%8E/
字节码执行引擎
概述
- 区别
- 物理机
- 建立在处理器、硬件、指令集、操作系统上
- 虚拟机
- 自己实现的,可以自行制定指令集,与执行引擎的结构体系
- 物理机
字节码执行引擎的执行方式
- 解释执行
- 编译执行
- 两者兼备
输入的是字节码文件,处理过程是字节码解析的等效过程,输出的是执行结果
运行时栈帧结构
- 用于支持虚拟机进行方法调用和方法执行的数据结构
- 属于虚拟机栈的元素
栈帧
局部变量表 local variable table
- 概述
- 用于存放方法参数和方法内定义的局部变量
- 一组变量的存储空间
- 容量
- 以变量曹为最小单位(variable slot)
- 作用:完成参数值到参数变量列表的传递过程
- 局部变量要手动赋值,否则会导致类加载失败
操作数栈Operand stack
- LIFO栈
- 栈的深度在编译时已经写到Code属性的max_stacks 数据项中
- 操作数栈中元素的数据类型必须与字节码指令的序列严格匹配
- java虚拟机的解释执行引擎成为基于栈的执行引擎,系统所指的栈就是值操作数栈
动态链接 Dynamic Linking
- 每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用。持有的这个引用是为了支持方法调用过程中的动态连接。
- 静态解析:一部分的符号引用在类加载阶段或者第一次使用的时候转化为直接引用
- 动态连接:符号引用在每一次运行期间转换为直接引用
方法返回地址
- 方法退出方式:
- 每一次的方法调用开始至执行完成的过程,都对应这一个栈帧在虚拟机栈里面从入栈到出栈的过程
- 在编译程序代码的时候,栈帧中需要多大的局部变量表、多深的操作数栈都已完全确定了,并且写入到方法表的Code属性当中
- 当前栈帧:位于栈顶的栈才是有效的
- 当前方法:当前栈帧关联的方法
方法调用
目标:确定被调用方法的版本(确认调用的是哪一个方法),不涉及方法内部具体运行过程
步骤如下:
解析(静态的过程)
含义
- 方法在运行之前有一个确定的调用版本,并且这个方法的调用版本在运行期是不可改变的。换句话说,调用目标在程代码中写好、编译器进行编译时就必须确定下来。这来方法的调用称为解析(resolution)
- 编译期可知,运行期不可变
- 类加载阶段解析
- 可以解析的方法:不能通过继承或者别的方式重写其他版本
- 方法在运行之前有一个确定的调用版本,并且这个方法的调用版本在运行期是不可改变的。换句话说,调用目标在程代码中写好、编译器进行编译时就必须确定下来。这来方法的调用称为解析(resolution)
调用方法的字节码指令
invokestatic 调用静态方法
invokespecial 调用实例构造
<init>
方法、是由方法和父类方法invokevirtual 调用虚方法
被final修饰的方法,虽然使用invokevirtual指令来调用,但是由于它无法被覆盖,没有其他版本,所以也无需对方法接收者进行多太选择,或者选择果肯定是唯一的。final修饰的方法是非虚方法
invokeiniterface 调用接口方法,会在运行时再确定一个实现此接口的对象
invokedyname 先在运行时动态解析出调用点限定符所引用的方法,然后在执行该方法
分派逻辑是由用户所设定的引导方法决定的
(
invokestatic, invokespecial, invokevirtual, invokeiniterface
),调用和分配逻辑时固化在虚拟机内部的非虚方法:静态方法、实例构造方法、父类方法
- 类加载的时候会把符号引用转化为直接引用(invokestatic 调用静态方法, invokespecial 调用实例构造
方法、是由方法和父类方法)
- 类加载的时候会把符号引用转化为直接引用(invokestatic 调用静态方法, invokespecial 调用实例构造
虚方法
- (`invokevirtual, invokeiniterface, invokedyname`)
分配(多态)
分配类型
静态分配
- 重载
- 所有依赖静态类型来定位方法执行版本的分派动作
- 静态分派发生在编译阶段
- 类型自动转换:char->int->long->float->double->….->变长参数
动态分配
- 重写
- 运行时根据实际类型确定方法的执行版本
- 动态分派是运行阶段
- 动态分派的实现
单分派与多分派
- 方法的宗量
- 方法的接收者
- 方法的参数
- 单分派
- 根据一个宗量对目标方法进行选择
- 多分派
- 根据多余一个宗量对方法进行选择
- 方法的宗量
java类型时静态多分派、动态但分派的语言
虚方法表(vtable)
动态类型语言支持
- 动态类型语言
- 类型检查时在运行期间完成的而不是编译器
- 静态类型语言
- 编译期进行类型检查
- 动态类型语言
语言类型
- 静态类型语言
- 在编译器确定类型,最显著的好处时编译期可以提供严谨的类型检查。这样与类型相关的问题能在编码的时候就能及时发现,利于稳定性及代码达到更大的规模
- 动态类型语言
- 行期确定类型,为开发人员提供更大的灵活性。动态语言实现可能会更加清晰简洁,提升开发效率
- java.lang.invoke包
- 使用MethodHandle 机制来动态调用方法
- invokedynamic指令
- 动态调用点
- 参数
- CONSTANT_InvokeDynamic_info
- 引导方法:Bootstrap Method
- 方法类型 MethodType
- 名称
- CONSTANT_InvokeDynamic_info
- 静态类型语言
基于栈的字节码解释执行引擎
- 解释执行
- 基于栈的指令集和基于寄存器的指令集
- 基于栈的解释执行过程
早期(编译期)优化
略
晚期(运行期)优化
略