1. 概述
执行引擎是Java虚拟机核心的组成部分之一。
JVM的主要任务是负责装载字节码到其内部,但字节码并不能够直接运行在操作系统之上,因为字节码指令并非等价于本地机器指令,它内部包含的仅仅只是一些能够被JVM所识别的字节码指令、符号表,以及其他辅助信息
那么,如果想要让一个Java程序运行起来,执行引擎的任务就是将字节码指令解释/编译为对应平台上的本地机器指令才可以。简单来说,JVM中的执行引擎充当了将高级语言翻译为机器语言的译者。
2. 内部结构
-
解释器:当Java虚拟机启动时会根据预定义的规范对字节码采用逐行解释的方式执行,将每条字节码文件中的内容“翻译”为对应平台的本地机器指令执行
-
JIT(Just In Time Compiler)编译器:就是虚拟机将源代码直接编译成和本地机器平台相关的机器语言
当Java虚拟机启动时,解释器可以首先发挥作用,而不必等待即时编译器全部编译完成后再执行,这样可以省去许多不必要的编译时间。随着时间的推移,编译器发挥作用,根据热点探测功能,将有价值的字节码编译成本地机器指令,获得更高的执行效率。
3. JIT编译器
-XX:CompileThreshold:配置方法调用计数器与回边计数器之和的阈值,当方法调用超过阈值时会向JIT编译器提交该方法的代码编译请求,该值client模式下默认是1500,Server模式下是10000次,默认热度衰减是开启的,如果不配置还会存在热度衰减的情况,参数:-XX:-UseCounterDecay
JIT也分为C1和C2,C1(client模式)64位操作系统只会用C2(server模式),C2的优化主要是在全局层面,逃逸分析是优化的基础。
C1编译器优化:
- 方法内联:将引用的函数代码编译到引用点处,这样可以减少栈帧的生成,减少参数传递以及跳转过程
- 去虚拟化:对唯一的实现类进行内联
- 冗余消除:在运行期间把一些不会执行的代码折叠掉
基于逃逸分析在C2上有如下优化
- 标量替换:用标量值代替聚合对象的属性值
- 栈上分配:对于未逃逸的对象分配对象在栈而不是堆
- 同步消除:消除同步操作,通常指synchronized
4. 程序执行方式
-Xint:完全采用解释器模式执行程序
-Xcomp:完全采用即时编译器模式执行程序。如果即时编译出现问题,解释器会接入执行
-Xmixed:采用解释器+即时编译器的混合模式共同执行程序
5. Graal编译器
-
自JDK10起,Hotspot又加入了一个全新的即时编译器:Graal编译器,未来可能会替换C2编译器
-
开关参数:-XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler
6. AOT编译器
- jdk9引入了AOP编译器(静态提前编译器,Ahead Of Time Compiler)
- Java 9引入了实验性AOT编译工具jaotc。它借助了Graal编译器,将输入的Java类文件转换为机器码,并存放至生成的动态共享库之中
- 最大的好处:Java虚拟机加载已经预编译成二进制库,可以直接执行。不必等待即时编译器的预热,减少Java应用给人带来“第一次运行慢”的不良体验
- 缺点:
- 破坏了Java“一次编译,到处运行”,必须为每个不同硬件、OS编译对应的发行包
- 降低了Java链接过程的动态性,加载的代码在编译期就必须全部已知
- 还需要继续优化中,最初只支持Linux x64 java base