如何确定Hotspot JVM决定再次重新编译JIT:ed代码的原因?

时间:2014-05-01 06:47:17

标签: java jit jvm-arguments jvm-hotspot

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

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

JVM标志如下(我只添加了-XX:+ PrintCompilation -verbose:class tp troubleshoot,其他是遗留的):

-Xms5g -Xmx5g -server -XX:+ AggressiveHeap -XX:+ UseFastAccessorMethods -XX:+ PrintGCDetails -XX:CompileThreshold = 100 -XX:-Citime -XX:-PrintGC -XX:-PrintGCTimeStamps -XX:+ PrintCompilation -verbose:类

#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 vs 2837),这意味着它被认为是"不同的"由JVM。

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

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

我尝试尽可能多地标记私有方法和类,但无济于事。

这是JRE 1.6.0_45-b06。

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

1 个答案:

答案 0 :(得分:13)

对于后人来说,一旦我阅读了热点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编译器确实决定为此函数生成本机代码时,它不会为if语句生成任何代码,因为该代码路径从来没有采取过。

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

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

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

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

所以我们只是放弃了#34;热身"我们的java代码,因为它不像有些人想的那么简单。

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

  • 易于维护(我们不需要在预热期间模拟生产并保持更新)
  • JIT不会根据我们的塑料模拟进行,而是根据生产行为(即使用JIT设计它而不是对抗它)

长期来看,客户可能需要为C / C ++等重写付费,以获得始终如一的低延迟,但这又是另一天。

编辑:我只想将更新添加到更新版本的热点JVM或"调整"围绕热点JVM参数永远不会解决此问题。它们都是烟雾和镜子。事实上,热点JVM从来没有为可预测的低延迟而编写,这个缺点是无法在java用户区内解决的。