Java Null检查消除可能导致错误代码?

时间:2017-09-04 19:39:39

标签: java optimization jvm null-check

最近,我阅读了here关于JVM优化的内容,这些内容非常棒 但我有一个优化问题,即空检查消除(或罕见陷阱)

总结空检查消除会删除if (obj == null) ...并希望获得最佳效果,而如果遇到分段错误则重新编译代码,此时间包括被忽视的if (obj == null) ...

我的问题是,鉴于:

bool foo(MyClass obj)
{
   if(obj == null)
      return false;
   m_someVar++;
   obj.doSomething(m_someVar);
}

因为Null-Check被删除,并且仅在m_someVar++之后才需要 当c为空时会foo执行m_someVar++吗?

编辑:

是否有一些来源可以更深入地解释这种优化的实现,这解释了这种优化如何使代码在语义上保持相同?

由于

2 个答案:

答案 0 :(得分:1)

有许多可能性来避免这个问题:在评论中已经提到了延迟或撤消增量。 JVM有一大堆技巧,其中一些适用:

  • obj.doSomething(m_someVar)是非虚方法调用(privatefinal方法,或从不重写的方法)时,它可能需要显式空检查(*)as没有SEGV,但必须在通话前抛出NPE。我假设该方法没有内联,否则应该在内联后应用此分析。
  • obj.doSomething(m_someVar)是虚拟方法调用时,您必须执行“方法调度”,即obj.getClass().getPointerToMethod("doSomething(int)")之类的内容,以确定要调用的具体方法。我写了“类似的东西”,因为这样做非常费时并且尽可能地进行优化。如果obj恰好是null,则此调度可以移动到增量之上,并且它本身将抛出NPE。
  • 如果你很幸运,那么发送可能看起来像if (obj.getClass() != MyClass.class) uncommon_trap();,这是最简单的情况(称为“单态呼叫站点”),但即使这样也涉及解除引用obj并再次获得NPE for null

(*)空检查可能看起来像obj.getClass(),它在汇编程序中是从相对于obj指针的固定偏移量加载的单个指令。当obj == null然后点击未映射的页面时。

答案 1 :(得分:0)

它认为你误解了实际关于零检查消除的内容。

实际上,它是关于在您(例如)调用实例上的方法时隐含的空检查的消除。

在您的示例中,不会消除显式if(obj == null)。那会(正如你所观察到的)改变程序的行为。消除空检查将是不正确的优化。

但是,以下 HERE 的隐式空检查:

bool foo(MyClass obj)
{
   if(obj == null)
      return false;
   m_someVar++;
   obj.doSomething(m_someVar);  // HERE
}

可以被删除,因为JIT编译器应该能够推断出obj在代码到达该点时始终是非空的。 (但这是一种不同的空检查优化。)

  

是否有一些来源可以更深入地解释这种优化的实施......

我不相信。至少,没有什么是明确的。 (除非你在JIT编译器源代码中计算可能的注释!)

未指定JIT优化程序行为。它基本上可以做任何事情,只要它不违反JLS中定义的Java语义。

  

...解释了这种优化如何保持代码在语义上相同?

正如我所解释的那样,您正在阅读的乐观空检查消除优化在您的示例中不适用。 (根据JLS,它使代码行为不正确。)