【问题标题】:Is there a way to tell from within the JVM whether a particular method has been JIT compiled?有没有办法从 JVM 中判断某个特定方法是否已被 JIT 编译?
【发布时间】:2023-08-15 07:48:02
【问题描述】:

在编写微基准测试时,可以观察到运行时的巨大差异,具体取决于方法是否已编译。有没有办法从程序中判断特定方法是否已编译?或者,有没有办法请求它,或者知道如何在没有任何额外信息的情况下充分加热它,例如传递给JVM的标志?显然,这不一定是完美的(例如,可能存在一些导致 JVM 回退到解释代码的条件),但这肯定是一种改进。

【问题讨论】:

  • 您可以凭经验测量方法的执行时间并检查结果的分布情况。当它被编译时,你通常会有一个跳转。关于这种方法有一个关于 SO 的问题,但我现在找不到。显然,这有点突兀。
  • 我几乎可以肯定答案是“不,绝对不是”。
  • @assylias - 只有在您确定该方法尚未编译时才有效。否则找不到跳转。此外,强大的变更检测算法也很麻烦。

标签: java performance jvm jit microbenchmark


【解决方案1】:

对于 Sun/Oracle JVM,您可以使用 -XX:CompileThreshold=1000 设置。

这 - 正如official documentation 所述 - 定义:

编译前的方法调用/分支数

然后,只需使用该数字来“预热”JVM。

您还可以将-XX:-PrintCompilation-XX:CompileThreshold 一起使用,以便在编译方法时(在控制台中)得到通知。

【讨论】:

  • JVM 计算的“分支”是什么?
  • @RexKerr -- JVM 尝试估计在方法中花费了多少时间(以及因此编译它有多少优势),主要是通过计算条目和向后分支(这将表示循环)。但可能还包括其他因素。
  • 好吧,这看起来是最好的答案,尽管它可能并不令人满意。谢谢!
【解决方案2】:

我很确定您可以打开日志记录,当方法被 JITCed 时显示。但我不知道从 Java 内部有什么方法可以告诉。

请记住,JIT 编译不是一个事件而是一个过程——一个方法可能会被重新编译多次,因为有关其特征的更多信息变得可用。

最后,请注意“热身”在一般情况下是不确定的。虽然您通常可以可靠地“预热”单个方法,但由于多种因素,即使是规模不大的应用程序也更难。

(虽然我不知道为什么无法将读取某种方法的某种 JITC 状态的功能添加到嵌入式调试工具中。)

补充:在对代码“sn-ps”进行基准测试时要注意的一件事是,执行所有循环的最外层方法通常不支持 JITC(取决于JITC 已实现),因为它永远不会返回,因此永远不能调用 JITCed 版本。因此,应始终将要进行基准测试的代码的“肉”放在重复调用的单独方法中,而不是将循环和要进行基准测试的代码放在同一方法中。

【讨论】:

  • 我确实指定了“微基准”。较大的应用程序更难确定。
  • 确实如此。还应该注意垃圾收集,您的测试用例是否具有预期的真实代码多次分派水平,结果是否被丢弃,因此代码可能被省略等等......即使是微基准也不容易正确!
相关资源