JVM如何决定JIT编译方法(将方法分类为" hot")?

时间:2016-02-24 12:10:09

标签: java jvm jit jvm-hotspot

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

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

我是否正确地假设每个方法都开始被解释,并且只要它没有被归类为"热方法"它不会被编译?我脑子里有一些东西,我读到一种方法被认为是热的"当它执行至少10.000次(解释方法10.000次后,它将被编译),但我不得不承认我不确定这个或我在哪里读过这个。

总结一下我的问题:

(1)是否每个方法都被解释,只要它没有被归类为" hot"方法(因此已被编译)或者是否存在方法被编译的原因,即使它们不是" hot"?

(2)JVM如何将方法分类为"非热"和"热"方法?执行次数?还有什么吗?

(3)如果某些阈值(如执行次数)为" hot"方法,是否有Java标志(-XX:...)来设置此阈值?

2 个答案:

答案 0 :(得分:53)

HotSpot编译策略相当复杂,特别是对于分层编译,它在Java 8中默认启用。既不是执行次数,也不是CompileThreshold参数。

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

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

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

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

    此处的关键参数为bTierXInvocationThreshold。根据编译队列的长度,可以针对给定方法动态调整阈值。

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

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

  • 特殊策略用于方法内联。微小的方法可以内联到呼叫者中,即使他们不是热的"。只有经常调用(TierXBackEdgeThresholdInlineFrequencyRatio)时,才能内联更大的方法。

答案 1 :(得分:9)

控制此问题的主要参数是-XX:CompileThreshold=10000

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

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

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

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

2)呼叫次数或迭代次数。

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