JIT热点代码检测

什么是热点代码?

简单说就是:同一段代码,被执行过很多次。

具体有两类:

1、被多次调用的方法。

2、被多次执行的循环体。

1好理解,2就是说一个方法内有循环体,可能循环次数很多。可能方法只被调用了几次,但是循环就执行了上万次。这类代码也要触发编译的。

1编译的时候就是把整个方法编译成本地代码,2是只把循环体编译成本地代码么?不是的,2也是会把整个方法编译成本地代码的。

为什么?

因为这种编译发生在方法执行过程中,因此形象的称为“栈上替换(On Stack Replacement)”,简称为OSR编译,即方法栈帧还在栈上,方法就被替换了。

上面提到的“多次”不是一个具体、可量化的用语,那多少次算达标呢?

另外,虚拟机是如何统计一个方法、一段代码执行的次数的呢?

我们先来看第二个问题,没错,是第二个。。

判断一段代码是不是热点代码,是不是需要触发即时编译,这样的行为称为热点探测(Hot Spot Detection),其实进行热点探测并不一定要知道方法具体被调用了多少次,目前主要的热点探测方式有两种:

1、基于采样的热点探测(Sample Based Hot Spot Detection):

  这种方法是虚拟机会周期性的检查各个线程的栈顶,如果发现某个方法经常出现在栈顶,那这个方法就是热点方法。

  优点:实现简单、高效,还可以获取方法调用关系(将调用栈展开即可);

  缺点:很难精确的确认一个方法的热度,容易受到线程阻塞或别的外界因素的影响而扰乱热点探测。

2、基于计数器的热点探测(Counter Based Hot Spot Detection):

  这种方法虚拟机会为每个方法(甚至是代码块)建立计数器,统计方法的执行次数,如果次数超过阈值就认为它是热点代码。

  优点:统计结果相对来说更加精确和严谨。

  缺点:实现起来麻烦,需要为每个方法建立并维护计数器,而且不能直接获取到方法的调用关系。

HotSpot虚拟机中使用的是第二种。因此它为每个方法准备了两类计数器:方法调用计数器(Invocation Counter)和 回边计数器(Back Edge Counter)。在确定虚拟机运行参数的前提下,两类计数器都有明确的阈值。

Tips:回边计数器是计算循环中执行代码次数的,“回边”是流程图中的术语。编译器中用于检测循环,想了解支配节点和回边可以参考这篇文章:https://www.jianshu.com/p/c01883fb619c

方法计数器的阈值:
Client模式下是1500次,Server模式下是10000次。

回边计数器的阈值:

Client模式下:

方法计数器阈值 * OSR比率 / 100

Server模式下:

方法计数器阈值 * ( OSR比率 - InterpreterProfile比率 ) / 100

OSR:OnStackReplacePercentage,栈上替换

InterpreterProfile:解释器监控

OSR比率默认值:140

解释器监控比率默认值:33

都取默认值的话:

client模式下回边计数器阈值:13995

server模式下回边计数器阈值:10700

 

如果不做任何设置,方法调用计数器统计的不是绝对次数,而是一个相对的频率。即一段时间内被调用的次数。

当超过一定时间后,如果方法调用次数仍不达到阈值,那方法调用计数器累加的次数就会减半,这个过程称为方法调用计数器热度的衰减(Counter Decay),这段时间就称为此方法统计的半衰周期(Counter Half Life Time)。

这里的“一定时间”是可以通过参数设置的:-XX: CounterHalfLifeTime,单位是秒。

热度衰减动作是在虚拟机进行垃圾收集时进行的。

可以关闭热度衰减:-XX: -UseCounterDecay,那方法调用计数器统计的就是绝对次数,这样只要系统运行时间够长,绝大部分方法都会被编译成本地代码。

 

以上就是关于JIT的大概介绍了,想了解更多的小伙伴可以读一读周志明大神的《深入理解Java虚拟机》中的第11章。本文中绝大多数知识都是源于这本书。

posted @ 2021-12-09 16:51  hucat  阅读(428)  评论(0编辑  收藏  举报