JVM系列实践:运行期优化

作者:陆金龙    发表时间:2022-12-24 01:20   

关键词:解释执行  即时编译  基于计数器的热点探测  

主要内容来自:周志明著,《深入理解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.