JVM如何决定对方法进行JIT编译(将方法分类为“热”)?

2022-09-01 03:34:07

我已经使用过 ,我知道JIT编译器的基本技术以及为什么使用JIT编译。-XX:+PrintCompilation

然而,我仍然没有发现JVM如何决定JIT编译一个方法,即“当正确的时间到来时,JIT编译一个方法”。

我假设每个方法都开始被解释,只要它不被归类为“热方法”,它就不会被编译,我是对的吗?我脑子里有一些东西,我读到一个方法在执行至少10.000次时被认为是“热的”(在解释该方法10.000次后,它将被编译),但我不得不承认我不确定这个或我在哪里读过这个。

所以总结一下我的问题:

(1)只要每个方法没有被归类为“热”方法(因此已经编译),是否每个方法都被解释,或者即使方法不是“热”,也有理由编译方法?

(2)JVM如何将方法分为“非热”和“热”方法?执行次数?别的东西?

(3) 如果“热”方法有一定的阈值(如执行次数),是否有 Java 标志 () 来设置此阈值?-XX:...


答案 1

HotSpot 编译策略相当复杂,特别是对于分层编译,这在 Java 8 中默认处于打开状态。这既不是执行次数,也不是参数问题。CompileThreshold

最好的解释(显然,唯一合理的解释)可以在HotSpot来源中找到,请参阅advancedThresholdPolicy.hpp

我将总结此高级编译策略的要点:

  • 执行从第 0 层(解释器)开始。
  • 编译的主要触发器是
    1. 方法调用计数器i;
    2. 背边计数器 .向后分支通常表示代码中的循环。b
  • 每次计数器达到特定的频率值 (, ),都会调用一个编译策略来决定下一步要对当前运行的方法执行的操作。根据 的值以及 C1 和 C2 编译器线程的当前负载,可以决定TierXInvokeNotifyFreqLogTierXBackedgeNotifyFreqLogib

    • 继续在口译员中执行;
    • 开始在解释器中进行分析;
    • 编译方法,C1位于第3层,具有进一步重新编译所需的完整配置文件数据;
    • 编译方法,C1位于第2层,没有配置文件,但可以重新编译(不太可能);
    • 最后,使用C1在第1层编译方法,没有配置文件或计数器(也不太可能)。

    这里的关键参数是 和 。可以根据编译队列的长度动态调整给定方法的阈值。TierXInvocationThresholdTierXBackEdgeThreshold

  • 编译队列不是 FIFO,而是优先级队列。

  • 具有配置文件数据(第 3 层)的 C1 编译代码的行为类似,只是切换到下一层(C2、第 4 层)的阈值要大得多。例如,解释的方法可以在大约200次调用后在第3层编译,而C1编译的方法在5000多次调用后在第4层进行重新编译。

  • 方法内联使用特殊策略。微小的方法可以内联到调用方中,即使它们不是“热的”。只有当经常调用时,才能内联稍大的方法(,)。InlineFrequencyRatioInlineFrequencyCount

答案 2

控制它的主要参数是-XX:CompileThreshold=10000

Hotspot for Java 8 现在默认使用分层编译,使用从级别 1 到 4 的多个编译阶段。我认为1不是优化。级别 3 是 C1(基于客户端客户端),级别 4 是 C2(基于服务器编译器)

这意味着一些优化可能会比您预期的更早发生,并且可以在达到10K阈值后很长一段时间内继续优化。我见过的最高是转义分析,在一百万次调用后消除了StringBuilder。

注意:循环迭代多次可以触发编译器。例如,10K次的循环就足够了。

1)直到一种方法被认为足够热,它才会被解释。但是,一些JVM(例如Azul Zing)可以在启动时编译方法,您可以强制Hotspot JVM通过内部API编译方法。Java 9也可能有一个AOT(提前)编译器,但它仍在研究AFAIK。

2) 调用次数或迭代次数。

3)是的,是主要的。-XX:CompileThreshold=


推荐