关于HotSpot JVM JIT的困惑

时间:2017-08-20 08:20:36

标签: java jvm jit hotspot

例如,一个方法中有10000次循环。当它运行1000次时,backingge_counter会触发JIT编译。解释器继续执行。当它循环 4000 时,JIT编译完成。

我的问题是,如何通过解释器执行余数6000 次或执行本机代码?或者在下次调用此方法之前不会执行本机代码? 下次调用此方法时会发生什么?

2 个答案:

答案 0 :(得分:9)

假设您正在询问HotSpot JVM,答案是剩余的交互将在已编译的代码中执行。

HotSpot JVM有一种称为“堆栈内替换”的技术,可以在方法运行时从解释器切换到编译代码。

http://openjdk.java.net/groups/hotspot/docs/HotSpotGlossary.html

  

堆叠更换
  也称为'OSR'。转换的过程   解释(或不太优化)堆栈帧到编译(或更多   优化的)堆栈框架。当解释器发现时会发生这种情况   一个方法循环,​​请求编译器生成一个特殊的   nmethod在循环中的某个入口点(具体地说,在a   向后分支),并将控制转移到该nmethod。粗略   与去优化相反。

如果您使用-XX:+PrintCompilation标志运行JVM,则OSR编译将标记为%符号:

    274   27       3       java.lang.String::lastIndexOf (52 bytes)
    275   29       3       java.lang.String::startsWith (72 bytes)
    275   28       3       java.lang.String::startsWith (7 bytes)
    275   30       3       java.util.Arrays::copyOf (19 bytes)
    276   32       4       java.lang.AbstractStringBuilder::append (29 bytes)
    276   31  s    3       java.lang.StringBuffer::append (13 bytes)
    283   33 %     3       LoopTest::myLongLoop @ 13 (43 bytes)
             ^                                    ^
            OSR                            bytecode index of OSR entry

<强>更新

通常在OSR编译之后,常规编译也会排队,因此下次调用该方法时,它将直接以编译模式运行。

    187   32 %     3       LoopTest::myLongLoop @ 13 (43 bytes)
    187   33       3       LoopTest::myLongLoop (43 bytes)

但是,如果在再次调用该方法时常规编译未完成,则该方法将在解释器中开始运行,然后将切换到循环内的OSR条目。

答案 1 :(得分:2)

让我们重申一下这个问题:

Java HotSpot编译器能否在执行过程中将方法从解释更改为已编译?

我认为可以。

这个引擎的任务并不容易(我在几年前为PalmOS掌上电脑工作时称为JUMP的Ahead-of-Time编译器时收集了该领域的一些经验)。当引擎决定切换时,它必须考虑以下几点,至少:

  • 程序计数器在哪里?在解释代码中,它位于从方法开头偏移的某个字节码处,确切地知道接下来要执行的字节码。在优化的本机代码中,通常JVM字节码不会转换为机器指令的隔离块,而是相互依赖,重新排列无序等等。因此,切换时可能没有与字节码程序计数器完全对应的本机指令地址。

  • 数据在哪里?解释器(可能)将所有内容保存在堆栈中,优化的本机代码使用寄存器和堆栈分配的混合,这对于本机转换中的不同位置将是不同的。

所以我读了the HotSpot whitepaper。它没有明确地回答这个问题,但在“去优化”下有一个暗示。当一个新类被动态加载或甚至在调试会话中被替换时,之前的优化(如内联)可能会变得无效。

  

因此,Java HotSpot VM必须能够动态地去优化   (如有必要,再重新优化)先前优化的热点,   即使在执行热点代码时也是如此。

这也是在编译代码和解释代码之间切换,只是反过来。由于这是HotSpot引擎的记录行为,我得出的结论是,可以在当前正在执行的方法调用中从解释代码切换到已编译代码。

编辑:

我对我所理解的问题的核心不够明确。

我知道有一种方法用10000次迭代进行循环,如下所示:

void loop() {
    for (int i=0; i<10000; i++) {
        // example loop body
        objects[i].doSomething();
    }
}

例如4000次迭代HotSpot编译器优化了该方法。那么会发生什么?

有两个方面,一个是微不足道的,一个是复杂的:

  • 微不足道的是,在循环内发生的调用(例如doSomething())将在可用时立即调用其编译版本。我在原来的答案中没有提到这一点,因为我认为这是理所当然的。

  • 复杂的方面是:当前正在运行的loop()执行是否会在i = 4000时从解释转换为编译代码?这就是我所理解的OP的问题。