性能调优专题-基础-1、JVM-编译器、解释器、执行器
1. JVM编译器
1.1. 前端编译器
如javac,将java文件编译为class文件。此阶段不会发生指令重排序。
1.2. 后端编译器
JIT 是将一些字节码编译为机器码,并存入 Code Cache,下次遇到相同的代码,直接执行,无需再编译。此阶段可能会发生指令重排序。 和解释器一样,起点都是编译后的 class 文件,而不是.java 源文件。
HotSpot中内置了两个即时编译器,分别称为 Client Compiler和 Server Compiler ,或者简称为 C1 编译器和 C2 编译器。目前的 HotSpot 编译器默认的是解释器和其中一个即时编译器配合的方式工作,具体是哪一个编译器,取决于虚拟机运行的模式,HotSpot 虚拟机会根据自身版本与计算机的硬件性能自动选择运行模式,用户也可以使用 -client 和 -server 参数强制指定虚拟机运行在 Client 模式或者 Server 模式。这种配合使用的方式称为“混合模式”(Mixed Mode),用户可以使用参数 -Xint 强制虚拟机运行于 “解释模式”(Interpreted Mode),这时候编译器完全不介入工作。另外,使用 -Xcomp 强制虚拟机运行于 “编译模式”(Compiled Mode),这时候将优先采用编译方式执行,但是解释器仍然要在编译无法进行的情况下接入执行过程。通过虚拟机 -version 命令可以查看当前默认的运行模式。
C2编译器是专门面向服务端的,并为服务端的性能配置特别调整过的编译器,也是一个充分优化过的高级编译器,几乎能达到 GNU C++ 编译器使用-O2 参数时的优化强度,它会执行所有的经典的优化动作,如 无用代码消除(Dead Code Elimination)、循环展开(Loop Unrolling)、循环表达式外提(Loop Expression Hoisting)、消除公共子表达式(Common Subexpression Elimination)、常量传播(Constant Propagation)、基本块冲排序(Basic Block Reordering)等,还会实施一些与 Java 语言特性密切相关的优化技术,如范围检查消除(Range Check Elimination)、空值检查消除(Null Check Elimination ,不过并非所有的空值检查消除都是依赖编译器优化的,有一些是在代码运行过程中自动优化 了)等。
2. JVM解释器
https://cloud.tencent.com/developer/article/1398588
hotspot解释器模块(hotspot\src\share\vm\interpreter
)有两个实现:基于C++的解释器和基于汇编的模板解释器。hotspot默认使用比较快的模板解释器。 其中
C++解释器 = bytecodeInterpreter*
+ cppInterpreter*
模板解释器 = templateTable*
+ templateInterpreter*
它们前者负责字节码的解释,后者负责解释器的运行时,共同完成解释功能。这里我们只关注模板解释器。
- C++解释器 =
bytecodeInterpreter*
+cppInterpreter*
零汇编:只和编译器有关,与CPU无关
volatile:语义就是禁止编译器优化
- 模板解释器 =
templateTable*
+templateInterpreter*
涉及CPU指令和汇编
volatile:lock的作用锁缓存行、刷MOB(ROB和StoreBuffer)
3. JVM执行器
解释执行:包括C++解释器和模板解释器。解释执行并不是每次执行字节码时动态把它编译成机器码,而是将根据字节码的类型,转到对应的机器码去执行,即一个派发(switch)的过程。而C++解释器派发到的是由字节码对应的C++代码所编译成的机器码,模板解释器派发到的是字节码对应的汇编模板所生成的机器码。由于C++代码由编译器编译成机器码,比较冗余,所以执行速度慢,而模板解释器的汇编模板是直接由汇编代码专门编写的,执行效率高。解释执行执行速度较慢,并不是每次将字节码动态编码生成机器码的原因,这是错误的。而是对于每个字节码都派发到对应的机器码上执行,而不是从上到下的顺序执行机器码,多了很多判断、跳转的指令,所以效率较低。
编译执行:JIT对于热点代码,编译成运行效率高的机器码。这里与模板解释器的区别在于JIT针对的是代码段生成机器码,而模板解释器是针对每个字节码指令生成机器码,以及JIT是动态生成的,模板解释器是在JVM启动时就把字节码对应的汇编模板转换为机器码。某种意义上模板解释器也属于JIT的范畴。当JIT把整段代码直接编译成机器码时,在执行时就可以自上而下的获取执行机器码,而不用对于每条字节码指令跳转到对应的机器码上,执行效率获得提升。
4. 参考与感谢
[[../../../../cubox/006-ChromeCapture/JVM的编译器和解释器(随笔) - 魅力峰值 - 博客园]]