Java打印编译输出: "made not entrant"和"made zombie"的含义是什么?

45

运行Java 1.6应用程序(1.6.0_03-b05)时,我添加了-XX:+PrintCompilation标志。对于一些方法的输出,特别是那些我知道会被频繁调用的方法,我看到了文本made not entrantmade zombie

这是什么意思?最好的猜测是,在重新编译该方法或具有更高优化级别的依赖项之前,进行了反编译步骤。真的是这样吗?为什么会出现“zombie”和“entrant”?

例如,以下是一些输出,其中某些行之间有相当长的时间间隔:

[... near the beginning]
42       jsr166y.LinkedTransferQueue::xfer (294 bytes)

[... much later]
42    made not entrant  jsr166y.LinkedTransferQueue::xfer (294 bytes)
---   n   sun.misc.Unsafe::compareAndSwapObject
170       jsr166y.LinkedTransferQueue::xfer (294 bytes)
170   made not entrant  jsr166y.LinkedTransferQueue::xfer (294 bytes)
  4%      jsr166y.LinkedTransferQueue::xfer @ 29 (294 bytes)
171       jsr166y.LinkedTransferQueue::xfer (294 bytes)

[... even later]
42    made zombie  jsr166y.LinkedTransferQueue::xfer (294 bytes)
170   made zombie  jsr166y.LinkedTransferQueue::xfer (294 bytes)
171   made not entrant  jsr166y.LinkedTransferQueue::xfer (294 bytes)
172       jsr166y.LinkedTransferQueue::xfer (294 bytes)

[... no further logs]
4个回答

25

我在我的博客上整理了一些关于此事的信息。我发现Cliff Click的评论如下:

僵尸方法是因为类加载使代码无效的方法。通常情况下,服务器编译器对非final方法做出积极的内联决策。只要内联方法从未被覆盖,代码就是正确的。当子类被加载并且方法被覆盖时,编译后的代码对所有将来的调用都是错误的。代码被声明为“未进入”(没有将来的调用到损坏的代码),但有时现有的调用者仍然可以继续使用该代码。在内联的情况下,这还不够好;当现有的调用者从嵌套调用返回代码时(或者只是在运行代码时),他们的堆栈帧会“取消优化”。当没有更多的堆栈帧将PC保留在损坏的代码中时,它被声明为“僵尸”——一旦GC处理完毕,就可以删除。


8
Kris Mok给JodaStephen写了一封回复,现在从他的博客链接过来,并且更加完整地描述了-XX:+PrintCompilation;这是链接: https://gist.github.com/1165804#file_notes.md - Blaisorblade

9

这绝对不是我的专业领域,但我很感兴趣,所以做了一些调查。

以下是您可能会感兴趣的一些链接:OpenJDK:nmethod.cppOpenJDK:nmethod.hpp

nmethod.hpp 的摘录:

// Make the nmethod non entrant. The nmethod will continue to be
// alive.  It is used when an uncommon trap happens.  Returns true
// if this thread changed the state of the nmethod or false if
// another thread performed the transition.
bool  make_not_entrant() { return make_not_entrant_or_zombie(not_entrant); }
//...

作为一个起点。


这是一个有用的开端,谢谢。我越看代码,就越发现我不知道的热点词汇越多!那么我们认为“not_entrant”是否仅意味着“不要再执行此编译代码,需要先进行去优化/重新编译”?另一个链接:请参见http://www.google.com/codesearch/p?hl=en#aRIt9pqzOVI/src/share/vm/oops/methodOop.cpp的第536行。 - Joe Kearney
4
根据“entrant”的含义,我的猜测是它标记编译方法,使其不会再次进入(尽管一些线程可能仍在其中),而“zombie”表示它没有进一步的用途并且可以被处理掉。就像收银柜台可以宣布它不接受新客户(非入口),但仍必须为排队等候的客户提供服务后才能关闭柜台(僵尸)。但这只是一个猜测。 - Mark Peters

9
一种较新的解释是日志中可能会出现以下条目:
129   72       3       EscapeAnalysysTest::second (24 bytes)
.......... some more lines
135   76       4       EscapeAnalysysTest::second (24 bytes)
137   74       3       EscapeAnalysysTest::second (24 bytes)   made not entrant

这实际上意味着一个方法(second)已经被编译成了第三层级别(见此链接),然后在第四层级别中进一步优化,并且它已经被设置为第三层级别的非入口点;这意味着它将被第四层级别的代码替换。


请问您能否提供一下这个解释的来源? - merovingienne
1
@merovingienne,当我阅读JVM源代码时,我记得这一点,但现在我不记得确切的路径了... - Eugene
1
@merovingienne 你可以在Douglas Hawkins的演讲中找到关于Java JIT非常有用的信息:Understanding the Tricks Behind the JIT。他在20:30解释了"made not entrant"。 - Jerry Lundegaard

3
这里有一个Gist,提供了大量关于PrintCompilation的信息。具体来说,它说:
当发生非正常退出时,如果决定使有问题的nmethod失效,首先会将其“不可用”,然后当NMethodSweeper在堆栈中找不到任何激活时,就会将其“变成僵尸”。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接