java - 我如何确定 Hotspot JVM 决定重新编译 JIT :ed code a second time? 的原因

标签 java jit jvm-arguments jvm-hotspot

我正在尝试为对延迟敏感的 Java 应用程序编写预热例程,以优化前几个事务,否则这些事务会因动态类加载和 JIT(主要)而变慢。

我面临的问题是,即使我的预热代码加载了所有类并通过多次调用它们(至少 100 次 -XX:CompileThreshold)来练习它们,稍后当实际用户登录这些相同的函数时仍然标记为“非进入者”并再次重新编译,这会导致延迟命中。

JVM flags如下(我只加了-XX:+PrintCompilation -verbose:class tp troubleshoot,其他都是legacy):

-Xms5g -Xmx5g -server -XX:+AggressiveHeap -XX:+UseFastAccessorMethods -XX:+PrintGCDetails -XX:CompileThreshold=100 -XX:-CITime -XX:-PrintGC -XX:-PrintGCTimeStamps -XX:+PrintCompilation -详细:类

#Warmup happens here
  12893 2351       my.test.application.hotSpot (355 bytes)
#Real user logs on here
 149755 2351      made not entrant  my.test.application.hotSpot (355 bytes)
 151913 2837       my.test.application.hotSpot (355 bytes)
 152079 2351      made zombie  my.test.application.hotSpot (355 bytes)

预热后没有类加载发生(我可以看到之前的类加载,所以标志正在工作)。

看起来该函数获得了一个新的 ID(2351 与 2837),这意味着它以某种方式被 JVM 视为“不同的”。

我如何确定 JVM 决定重新编译此函数的原因?

我想这归结为我如何确定 ID 更改的原因?标准是什么?

我尝试将尽可能多的方法和类标记为私有(private),但无济于事。

这是 JRE 1.6.0_45-b06。

感谢任何有关如何排除故障或获取更多信息的提示! :)

最佳答案

对于后人来说,一旦我阅读了热点 JVM 的一些源代码,它就相当简单了。

以下标志将指出导致函数被取消优化和重新编译的确切源代码行:

-XX:+TraceDeoptimization -XX:+WizardMode -XX:+PrintNativeNMethods -XX:+PrintDependencies -XX:+DebugDeoptimization -XX:+LogEvents

通常是这样的 if 语句。

void function (Object object){
    if ( object == null ){
        // do some uncommon cleanup or initialization
    }
    do_stuff();
}

假设我的预热代码从未触发 if 语句。

我曾假设整个函数会一次性编译,然而,当 JIT C2 编译器确实决定为该函数生成 native 代码时,它不会为 if 语句生成任何代码,因为该代码路径从未被带走。

它只会在C2编译器线程中生成一个条件分支,生成陷阱和异常处理程序。我认为发生这种情况是因为 native 代码缓存非常小,因此 JVM 编写者不想用可能无用的代码填充它。

无论如何,如果语句为真(即对象永远为空),那么该函数将立即无条件地触发此异常处理并重新编译(导致卡住/延迟按一对顺序命中毫秒)。

当然,我的预热代码不会以与生产完全相同的方式调用每个函数,我敢猜测,在任何复杂的产品中,这几乎是不可能的,而且无论如何都是维护噩梦。

这意味着为了有效地预热 Java 应用程序,代码中的每个 if 语句都需要由预热代码调用。

因此我们将简单地放弃“预热”我们的 Java 代码的想法,因为它并不像某些人认为的那么简单。

出于以下原因,我们将重新编写部分应用程序以支持一次运行数周/数月:

  • 更容易维护(我们不需要在预热期间模拟生产并保持更新)
  • JIT 不会根据我们的塑料模拟来完成,而是根据生产行为来完成(即,将 JIT 用于它的设计目的,而不是与之抗争)

从长远来看,客户可能会为使用 C/C++ 或类似语言进行重写付费,以获得始终如一的低延迟,但那是以后的事了。

编辑:让我补充一点,更新到更新版本的热点 JVM 或围绕热点 JVM 参数“调整”将永远无法解决此问题。既是雾里看花,又是镜花水月。事实上,热点 JVM 从来没有为可预测的低延迟而编写,而且这个缺点不可能在 Java 用户空间内解决。

关于java - 我如何确定 Hotspot JVM 决定重新编译 JIT :ed code a second time? 的原因,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23403318/

相关文章:

java - 如何将 Kafka 数据源中的值转换为给定的模式?

java - 如何配置JVM只在真正需要的时候才分配内存?

java - 将 Angular 5 应用程序嵌入到 Jsp 项目的另一个应用程序中

java - 将 double 存储到二维数组中

java - 将 HttpClient 响应转换为 JsonObject 时出错

java - 为什么需要指定 Java 堆大小?

php - 在 PHP 7.4 上预加载和 JIT(即将在 PHP 8 上发布)有什么区别

c# - 是否有编译为机器代码的 C C++ C# 编译器

python - CUDA 不支持边界检查