例如,方法中循环10000次。当运行1000次时,backedge_counter会触发
JIT
编译。并且解释器继续执行。当循环4000次时,JIT
编译完成。
我的问题是,剩余的6000次是如何由解释器执行,或者执行本机代码?或者直到下次调用该方法才执行本机代码? 下次调用这个方法时会发生什么?
已编译代码中执行。
HotSpot JVM 有一种称为“栈上替换”的技术,可以在方法运行时从解释器切换到编译代码。
http://openjdk.java.net/groups/hotspot/docs/HotSpotGlossary.html
如果您使用堆栈替换 也称为“OSR”。转换的过程 将堆栈帧解释为已编译的(或更多) 优化)堆栈框架。当口译员发现 一个方法正在循环,要求编译器生成一个特殊的 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 条目。
Java HotSpot 编译器是否能够在执行过程中将方法从解释更改为编译?
我觉得可以。
对于引擎来说,这项任务并不容易(几年前,我在为 PalmOS 手持设备开发名为
JUMP 的提前编译器时积累了一些该领域的经验)。当引擎决定切换时,至少必须考虑以下几点:
HotSpot 白皮书。它没有明确回答这个问题,但在“去优化”下有一个提示。当动态加载新类甚至在调试会话中替换新类时,以前的优化(如内联)可能会变得无效。
因此Java HotSpot VM必须能够动态去优化 (然后重新优化,如有必要)先前优化的热点, 即使在执行热点代码时也是如此。这也是编译代码和解释代码之间的切换,只是反过来而已。由于这是 HotSpot 引擎的记录行为,我得出的结论是,在当前执行的方法调用中从解释代码切换到编译代码是可能的。
编辑:
我对问题核心的理解不够明确。
我知道有一种方法可以进行 10000 次迭代,如下所示:
void loop() {
for (int i=0; i<10000; i++) {
// example loop body
objects[i].doSomething();
}
}
例如之后HotSpot 编译器已经优化了该方法 4000 次迭代。然后会发生什么?
有两个方面,一琐碎一复杂:
doSomething()
)将在其编译版本可用时立即调用它。我在原来的答案中没有提到这一点,因为我认为这是理所当然的。
loop()
执行会在i=4000时从解释代码切换到编译代码吗?这就是我所理解的OP的问题。