笔记目录
1.解释执行与JIT及时编译
Java程序在运行的时候,主要是执行字节码指令,一般情况这些指令都是通过顺序的解释执行。如果某一些代码的执行评率特别高,JVM就引入了即时编译器来优化这些热点代码,提高java运行效率。
2.即时编译器类型
2.1 C1编译器
C1编译器是一个简单快速的编译器,主要关注局部性的优化,适用于执行时间短和启动要求快的程序,例如GUI相关的程序,C1编译器只做编译工作,几乎不做优化任务。
2.2 C2编译器
C2编译器目的是为了长期运行的程序做调优的编译器,成为Server Compiler编译器。
3.热点探测
所谓热点就是执行频率相当高的代码,这些代码由于使用非常频繁所以需要JIT优化,首先得探测到什么代码是热点代码,Hotspot使用了2中方式探测:
3.1 方法调用计数器
统计方法被调用的次数,在客户端模式下默认1500,服务端模式默认10000次,可以通过-XX:CompileThreshold
来设定这个阈值。
3.2 回边计数器
统计一个循环代码的执行次数,例如for、while等。在不开启分层变异模式下,服务端模式默认是10700次。
JIT即时编译的代码会缓存在元空间中,这个CodeCache大小可以配置,默认只有32M,一旦空间满了以后JIT将不会清理和淘汰。
4.分层编译
在JDK7之前,需要程序根据特性来选择对应的JIT编译器,JDK7以后引入了分层编译,综合了C1的启动性能优势和C2的峰值性能优势。
在分层编译模式下-XX: CompileThreshold
参数将失效,而是动态调整待编译和线程数量,当方法调用计数和回边技术之和超过了阈值,就会触发JIT编译。JDK8默认开启分层编译。
JIT分层编译执行状态分为5个层次:
第0层:JVM解释执行,默认开启性能监控,如果不开启,则可触发第二层编译。
第1层:C1编译,将字节码编译为本地代码、提高程序运行效率。
第2层:C1编译,开启性能监控,仅执行热点探测合格的热点代码的编译。
第3层:C1编译,执行所有带性能监控的C1编译。
第4层:C2编译,将字节码编译为本地代码,并优化代码。
5.编译优化技术
5.1 方法内联
JIT分析方法的结构,如果方法能够合二为一,将调用的方法合并到本方法体以内,就避免了一次方法调用所带来的的额外开销,提高了程序运行效率。但是只有方法体大小<35字节才会进行内联,可以通过-XX:FreqlnlineSize
来设置方法体大小。
5.2 锁消除
JDK8默认开启锁消除功能,就是在单线程环境下,局部变量的一些锁修饰将会消除掉,例如在方法内部的StringBuffer就会被JIT优化掉他的锁。
5.3 标量替换
JIT会分析一个对象是否能够逃出当前方法,进而进行逃逸分析,如果这个对象还能够被拆分的话,JVM就不会在堆内存中进行分配而是将其拆解的变量分配在栈中就是标量替换。JDK8默认开启标量替换。