目录
一 JIT 基础
JIT全程Java Intime Compiler,即Java即时编译器。
解释执行:由解释器一行一行翻译执行,
编译执行:把字节码(class文件)编译器机器码(二进制)直接执行机器码。
javac会把.java文件编译成.class文件,所以我们说Java是编译型语言。当然Java是强类型的语言,通常我们说强类型的是编译型的,弱类型的脚本语言(也叫动态语言,相对应的强类型语言叫静态语言)。.class文件格式就是“字节码”
JIT的动机基于“二八定律”,20%的热点代码占据了程序80%的执行时间.
二查询运行模式
java -version
mixed mode 为混合模式
java -Xint -version
-Xint 设置jvm执行模式为解释器执行
-Xcomp jvm 优先以编译模式运行,不能编译的以解释器执行
-Xmixed 混合模式执行
三 hotspot 的 JIT
一般代码以解释器执行,当虚拟机发现某个方法或代码块的运行特别频繁的时候,就会认为这些代码是热点代码。为了提高热点代码的效率,会用JIT 把这些代码(class)编译成 机器码(二进制),并进行各层次优化。
3.1 C1编译器 client compiler
简单快速的编译器,主要关注局部性的优化,适用于执行时间较短或对启动性能有要求的程序,例如Gui对启动时间有要求
3.2 C2编译器 server compiler
长期运行在服务端的程序做性能调优的编译器,适用于执行时间较长,或对峰值有要求的程序,
3.3 分层编译
- 0 解释执行
- 1 简单c1编译:会使用C1编译器进行一些简单的优化,不开启Profiling(jvm性能监控)
- 2 受限的C1编译:仅执行带方法和调用的次数以及循环回边执行次数Profiling的C1编译
- 3 完全C1编译:会执行带有所有Profiling的C1代码
- 4 C2编译:使用C2编译进行优化,该级别会启用一些编译时较长的优化,一些情况下会根据性能监控信息进行一些非常激进的性能优化
级别越高,应用启动的越慢,优化的开销越高,峰值性能也越高。
3.4 如何找到热点代码
- 基于采样的热点探测(周期检测各个线程的栈顶,如果发现某些发放在栈顶,这个方法是热点方法)
- 基于计数器的热点探测(hotspot采用此方法)
- 回边计数器(back edge Counter):用于统计一个方法中循环代码的执行次数,在字节码中遇到控制流向后跳转的指令称为“回边”,在不开启分层编译的情况下,在C1编译器下的默认阈值是 13995 次,在C2模式下默认是 10700 次。可使用-XX:onstackReplacePercentage=X指定阈值。
方法调用计数器(invacation counter) :用于统计方法被调用的次数,在不开启分层编译的情况下,在C1编译器下的默认阈值是1500次,在C2模式下默认是10000次。也可以用-XX:compileThreshold=X指定阈值。
开启分层编译的情况下,2种计数的阈值会根据当前待编译的方法数和以及编译的线程数来动态调整阈值。(默认开启分层编译)
3.5 方法调用计数器 和 回边计数器 的流程图
四 编译器优化 方法内联
1. 把目标方法的代码直接复制到发起调用的方法之中,避免发生真实的方法调用。
2.条件
- 方法体足够小
- 被调用的方法被运行时的实现被可以唯一确定,比如尽量使用final private static 关键字修饰方法,避免因为多态,需要对方法做额外的检查