执行引擎概述
执行引擎是Java虚拟机核心的组成的部分之一
- 虚拟机的执行引擎是有软件自行实现的,不受物理条件制约的定制指令集与执行引擎结构体系,能执行不被硬件支持的指令集格式。
- JVM主要任务是装在字节码到其内部
- 执行引擎的主要任务就是:将字节码指令解释/编译(后端编译。生成字节码的编译成为前端编译)为本地机器指令
- JVM中的执行引擎重放了将高级语言翻译为机器语言的翻译者
Java代码编译和执行过程
大部分的程序代码转换为物理机的目标代码或者虚拟机能执行的指令集之前,都需要执行上述步骤。
什么是解释器(Interpreter)?
字节码采用逐行解释的方法执行。
什么是JIT编译器?
JIT (JUST IN TIME Complier)编译器:就是虚拟机将源代码直接编译成本地机器平台相关的机器语言。
机器码、指令、汇编语言
机器码
- 采用二进制编码方式表示的指令,就是机器指令码。开始人们就是采用它编写程序,这就是机器语言
- 机器语言虽然能被计算机理解和接受,但是人们的语言差别太大,不容易被人们理解和机器,并且用他们编程容易出错
- 执行速度最快
- 机器指令予CPU紧密相关,所以不同种类的CPU虽对弈的机器指令也就不同。
指令
- 由于器码是有0和1组成的二进制序列,可读性太差。于是有了指令
- 指令就是把机器码中特定的0和1序列,简化成了对应的指令(一般是英文简写如mov、inc),可读性稍稍好。
指令集合
- 不同的硬件平台,各自支持的惠玲,是有差别的,因此每个平台所支持的指令称之为对应不同的指令集
- 常见的
- x86指令集,对应的是x86架构的平台
- ARM指令集。对应是ARM架构的平台
汇编语言
- 由于指令可读不好,有了汇编语言
- 汇编语言中,用助记符(Mnemonics)代替机器指令的操作码,用地址符号,或者标号代替指令或操作数的地址
- 汇编语言编写的陈旭必须翻译成机器指令码,计算机才能识别。
字节码
- 字节码是一种中间状态的二进制代码(文件),比机器码更加抽象,需要直译器转译才能执行成机器码
- 字节码主要是为了实现特定的软件运行和软件坏境(与硬件无关)
- 字节码实现方式都是通过编译器和虚拟机器,编译器将源码翻译成字节码。
解释器
- 运行时的翻译者,将自己阿玛文件中的内容翻译为对应平台的本地机器码指令执行
- 当一条执行完成后,再根据PC寄存器中记录的效益较需要被执行的字节码指令执行解释操作。
解释器分类
- 字节码解释器
- 模板解释器
现状
基于解释器执行已经沦落为抵消的代名词
为了解决这个问题,JVM支持了一种即时编译的技术。
避免函数被解释执行,将整个函数整体编译成机器码,每次函数执行时,只执行编译后的机器码即可。
JIT编译器
HotSpot 采用解释器与即时编译器并存
为什么要保存解释器?
- 程序启动解释器立即马上执行。(省略编译时间)
- 解释执行在编译器进行激进优化不成立时,作为编译器的逃生门
编译器不确定性
- Java语言的编译器其实是一段不确定的操作过程,因为它可能是指一个前端编译器(编译器前端)===>把.java转换为.class文件的过程
- 可以能指的是后端运行编译器(JIT编译器)====>把字节码转换为机器码的过程。
- 会还可能是金泰提前编译器(AOT编译器,Ahead of Time Compiler)直接把.java文件变异成本地机器代码的过程。
如何选择?
热点代码及探测方式
- 是否需要使用JIT编译器直接将字节码便以为对应平台的机器指令,需要看执行的频率而定。
- 关于那些需要被便以为本地代码的字节码,也被成为热点代码
- JIT编译器在运行时会针对那些频率搞得热点代码做出深度优化
什么是热点代码
- 多次调用的方法,或者是一个方法体内部循环次数较多的选题都可以成为热点代码。
- JIT编译器将这些热点代码便以为本地机器指令执行,依靠热点探测功能
- 目前HotSpot VM 采用的热点探测方式是基于计数器的热点探测
- 基于计数器的热点探测虚拟机加会简历两个不同类型的计数器,分别是方法调用计数器(Invocation Counter)和回边计数器(Back Edge Counter)
- 方法调用计数器用于统计方法的调用次数
- 回边计数器则用于统计循环体执行的循环次数
方法调用计数器
- 要触发JIT编译,必须要达到阈值****Clinet模式是1500次,Server模式是10000次
- 这个阈值可以通过虚拟机参数-XX:ComileThreshold认为舍弟
- 当一个方法被调用时,会先检查改方法是否被JIT编译过得版本,如果存在,则优先使用编译后的本地代码来执行,如果不存在,就调用技术区值+1,然后判断方法调用计数器与回边计数器值之和。如果超过这个阈值,就会转换为JIT
热度衰减
一段石家街之内方法被调用的次数,当超过一定的时间限度,就会减少一半。
- 关闭热度衰减参数
-XX:-UseCounterDecay - 设置半衰周期参数
-XX:CounterHalfLifeTime
回边计数器
统计一个方法中循环体的执行次数
设置程序执行方式
完全采用解释器执行还是完全采用即时编译器执行 可以通过下列参数设置。
-Xint :完全采用解释器模式执行
-Xcomp:完全采用即时编译器模式执行程序。如果即时编译器出现问题。解释器会介入
-Xmixed: 采用解释器+即时编译器的混合模式共同执行程序。
HotSpot VM中JIT分类
-Client模式,使用C1编译器
C1编译器会对字节码进行简单和可靠的优化、耗时短,以大奥更快的编译速度
-Server模式,使用C2编译器
C2编译器进行耗时较长,以及更寂静优化,但是执行效率更高
C1和C2编译器不同的优化策略
- C1:在不同编译器上有不同的优化策略,C1编译器主要有方法内敛,去虚拟化,冗余消除。
- C2 :主要是全局层面,逃逸分析是优化策略的基础,基于挑一挑一分析C2有如下几种优化
- 标量提花:用变量值代替聚合对象的属性值
- 栈上分配:对于为逃逸的对象分配对象在栈上而不是堆
- 同步消除:清除消除操作,通常知synchronized
- 分层编译策略:程序解释执行(不开启性能监控)可能触发C1将字节码翻译成机器码,可以简单优化。
小结
- JIT编译出的机器码一般比解释器高
- C2编译器启动时间长比C1慢,但是系统稳定执行后,C2编译器执行速度远远快于C1编译器
最后
Graal编译器
- JDK10起,HotSpot加入一个全新的即时编译器:Graal编译器未来可期待
- -XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler去激活 ,就可以使用了。
AOT编译器
与JIT并列(提前编译器)
- JDK9映入AOT编译器(静态提前编译器,Ahead Of Time Compiler)
- JDK9引入实验性AOT 编译工具jaotc,借助Graal编译器,将所输入的Java类文件转换为机器码
- 程序运行之前就将字节码转为机器码
AOT编译器优劣分析
- 好处
Jaba虚拟机预编译成二进制库,不必等待编译器预热,减少第一次运行慢的不良体验
不足
破坏了一次编译导出运行。
降低了Java链接过程中的动态性