最近,我阅读了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++
吗?
编辑:
是否有一些来源可以更深入地解释这种优化的实现,这解释了这种优化如何使代码在语义上保持相同?
由于
答案 0 :(得分:1)
有许多可能性来避免这个问题:在评论中已经提到了延迟或撤消增量。 JVM有一大堆技巧,其中一些适用:
obj.doSomething(m_someVar)
是非虚方法调用(private
或final
方法,或从不重写的方法)时,它可能需要显式空检查(*)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,它会使代码行为不正确。)