JVM系列实践:运行期优化
主要内容来自:周志明著,《深入理解Java虚拟机》
1.解释器与编译器
在部分的商用虚拟机如Sun HotSpot、IBM J9中,Java程序最初是通过解释器进行解释执行。
后续发展成解释器与即时编译器并存的架构。
虚拟机将运行特别频繁的方法或代码块认定为热点代码(Hot Spot Code)。即时编译器把热点代码编译成与本地平台相关的机器码,进行各种层次的优化。
解释器与编译器并存的优势:
(1)程序需要迅速启动和执行时,解释器可以首先发挥作用,让程序立即执行,省去编译时间。
(2)随着程序运行时间的增加,编译器逐渐发挥作用,把越来越多的代码编译成本地代码,可以获取更高的执行效率。
(3)当程序运行环境内存资源限制较大时,使用解释器执行可节约内存。
(4)解释器执行可作为编译器激进优化的一个逃生门,当激进优化失效,可以通过逆优化退回到解释状态继续执行。
2.Hot Spot即时编译器
解释器与编译器搭配使用的方式称为“混合模式”。用户可以使用参数“-Xint”强制虚拟机运行于“解释模式”,也可以使用参数“-Xcomp”强制虚拟机运行于“编译模式”(但解释器仍要在编译无法进行时介入执行过程)。
HotSpot虚拟机内置了两个即时编译器:Client Compiler(简称C1)和Server Compiler(简称C2)。虚拟机根据自身版本和宿主机的硬件性能自动选择运行模式,用户也可以使用 “-client”或“-server”参数强制指定虚拟机运行在C1或C2模式。
C1将字节码编译成本地代码,进行简单、可靠的优化,有必要则加入性能监控的逻辑。
C2将字节码编译成本地代码,启用编译耗时较长的优化,根据性能监控进行一些不可靠的激进优化。
3.编译对象与触发条件
(1)编译对象,即被编译器即时编译的“热点代码”有两类:
被多次调用的方法。
被多次执行的循环体。
(2)热点代码的判断
热点探测方法有基于采样的热点探测和基于计数器的热点探测。
Hot Spot中使用基于计数器的热点探测方法。针对两类编译对象提供了方法调用计数器(Invocation Counter)和回边计数器(Back Edge Counter)两类计数器。
(3)即时编译的触发
计数器达到阈值,触发即时编译。
方法调用计数器:默认阈值在Client模式下是1500次,在Server模式下是10000次。这个阈值可以通过-XX:CompileThreshold设定。这里的次数不是绝对的,因为存在热度衰减动作。
回边计数器:统计一个方法中循环体代码执行次数。建议回边计数器统计是为了触发OSR编译(栈上替换)。阈值通过-XX: OnStackReplacePercentage来间接调整回边计数器的阈值。回边计数器没有热度衰减。回边计数器阈值的计算公式:
Client模式下公式:方法调用计数器阈值(CompileThreshold)x OSR比率/100。OnStackReplacePercentage的默认值是933,CompileThreshold也取默认值,计算的阈值是13995.
Server模式下公式:方法调用计数器阈值(CompileThreshold)x OSR比率 - 解释器监控比率(InterpreterProfilePercentage)/100。OnStackReplacePercentage的默认值是140,InterpreterProfilePercentage默认值是33,CompileThreshold也取默认值,计算的阈值是10700.