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

虚拟机字节码执行引擎

 

 

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

  • 概述

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

物理机执行引擎

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

直接建立在处理器,硬件,指令集和操作系统层面上

虚拟机执行引擎

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

可以自行制定指令集与执行引擎的结构体系

不同实现

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

解释执行

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

通过解释器执行

编译执行

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

通过即时编译器产生本地代码执行

两者兼备

  • 运行时栈帧结构

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

局部变量表

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

是一组变量值存储空间,用于存放方法参数和方法内的局部变量

java程序编译为Class文件时,在方法的Code属性的max_locals中确定了局部变量表的最大容量

局部变量表以变量槽Slot为最小单位

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

Slot应该能够存放一个boolean,byte,char,short,int,float,reference或returnAddress类型数据

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

reference类型至少能做到的两点

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

从此引用中直接或间接地查找到对象在Java堆中起始地址

能直接或间接的查找到对象所属数据类型在方法区中存储的类型信息

returnAddress服务的jsr,jsr_w,和ret指令已由异常表替代

64位数据类型,虚拟机会以高位对齐方式为其分配两个连续Slot,由于局部变量表是线程私有的,所以无论读写两个连续Slot是否是原子操作,都不会引起数据安全问题

局部变量表的Slot是可重用的

使用64位物理内存空间实现Slot需要使用对齐和补白手段

虚拟机使用局部变量表完成参数值到参数变量表的传递

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

实例方法第0位索引Slot默认用于传递对象实例的引用,可用this访问

其余参数按照参数表顺序占用从1开始的Slot排列

最后根据方法内部定义的局部变量顺序和作用域分配其余Slot

局部变量表不像类变量那样存在赋0值的“准备阶段”

操作数栈

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

栈元素可以是任意Java数据类型

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

32位数据类型栈容量为1

64位数据类型栈容量为2

操作数栈深度不会超过max_stacks

栈元素数据类型必须与字节码指令序列严格匹配

大多数虚拟机栈帧会有一部分重叠,以优化方法调用

动态连接

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

每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用

静态解析是指符号引用在类加载阶段或第一次使用的时候转化为直接引用

动态连接是指符号引用在每一次运行期间转化为直接引用

方法返回地址

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

退出方法的方式

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

正常完成出口,遇到方法返回字节码指令,是否有返回值由返回指令决定

异常完成出口,遇到了异常且没有在方法体内处理,没有返回值

返回到方法被调用位置

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

正常退出,调用者的pc计数器值可作为返回地址

异常退出,返回地址通过异常表确定

附加信息

  • 方法调用

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

解析

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

类加载的解析阶段会将一部分符号引用转化为直接引用的前提是方法在程序运行之前就由一个可确定版本,且运行期不可改变,这类调用称为解析

编译期可知,运行期不变

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

静态方法

私有方法

只要能被invokestatic和invokespecial指令调用的方法,都可以在解析阶段确定唯一版本

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

静态方法

私有方法

实例构造器

父类方法

分派

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

静态分派

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

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

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

静态类型

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

Human man =new Man(),Human是man的静态类型

字面量没有显式的静态类型,造成了重载版本并不唯一

实际类型

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

Human man =new Man(),Man是man的实际类型

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

静态分派发生在编译阶段,因此确定静态分派的动作不是虚拟机执行的

动态分派

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

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

和动态多态--重写关系密切

invokevirtual指令运行时解析过程

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

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

若C中找到与常量中的描述符和简单名称都相符的方法,则进行访问权限校验,若通过则返回这个方法的直接引用,否则抛出异常

否则依次从下往上对C父类进行第二步

否则,抛出异常

单分派和多分派

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

方法接收者和方法参数统称为宗量

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

多分派根据多于一个宗量对目标方法选择

java是一门静态多分派,动态单分派的语言,编译时根据参数类型和接收者确定方法版本,运行时根据接收者实际类型确定方法版本

虚拟机分派的实现

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

虚方法表

接口方法表

动态类型语言支持

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

动态类型语言

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

关键特征是它的类型检查的主体过程是在运行期而不是编译期

变量无类型,而变量值才有类型

动态类型语言只有在运行期才能确定接收者类型

jdk1.7与动态类型语言

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

jdk1.7以前的方法调用指令的第一个参数都是在编译时产生的被调用方法的符号引用

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

invokevertual

invokespecial

invokestatic

invokeinterface

在此之前在java虚拟机上实现的动态类型语言方式蹩脚,影响性能

jdk1.7出现了invokedynamic指令以及java.lang.invoke包

java.lang.invoke包

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

提供了MethodHandle动态确定目标方法的机制,类似于函数指针

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

MethodHandle与Reflection的区别

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

Reflection是在模拟java代码层次的方法调用,MethodHandle是在模拟字节码层次的方法调用

Reflection中的java.lang.reflect.Method对象远比MethodHandle机制中的java.lang.invoke.MethodHandle对象包含的信息多

虚拟机可以在MethodHandle机制方面在字节码层面做各种优化

MethodHandle可以服务于所有Java虚拟机之上的语言

invokedynamic指令

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

每一处含有invokedynamic指令的位置都称作"动态调用点",第一个参数不再是代表方法符号引用的CONSTANT_Method_info常量,而是变为jdk1.7新加入的CONSTANT_Method_info常量,从这个常量可以得到三项信息

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

引导方法

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

有固定的参数,返回值是java.lang.invoke.CallSite对象,代表真正要执行的目标方法的调用

方法类型

方法名称

掌握方法分派规则

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

jdk1.7前4条调用指令的分派逻辑固化在虚拟机中程序员无法改变

invokedynamic它的分派逻辑不是由虚拟机决定而是由程序员决定

  • 基于栈的字节码解释引擎

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

解释执行

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

java程序的编译是半独立的实现

解释器负责解释执行

即时编译器负责产生本地代码编译执行

基于栈的指令集与基于寄存器的指令集

基于栈的解释器执行过程