Java Null check elimination can cause bad code?

1k views Asked by At

Recently, I read here about JVM optimizations , which are great,
but i have a problem with one optimization, namely Null Check Elimination (or Uncommon trap)

To sum up Null Check Elimination removes a if (obj == null) ... and hopes for the best, while if encounters a Segmentation Fault it recompiles the code and this time includes the neglected if (obj == null) ....

My question is, Given:

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

because the Null-Check is eliminated, and required only after m_someVar++.
will foo execute m_someVar++ an extra time when c will be null?

Edit:

Is there some source for deeper explanation on the implementation of this optimizations, which explain how does this optimization keeps the code semantically same?

Thanks

1

There are 1 answers

0
maaartinus On BEST ANSWER

There are many possibilities ho to avoid the problem: Delaying or undoing the increment was already mentioned in a comment. The JVM has a big bag of tricks and some of them applies:

  • When obj.doSomething(m_someVar) is a non-virtual method call (a private or final method, or a never overridden method), then it probably requires an explicit null check(*) as there would be no SEGV, but the NPE must be thrown before the call. I'm assuming the method was not inlined, as otherwise this analysis should be applied after inlining.
  • When obj.doSomething(m_someVar) is a virtual method call, then you have to do "method dispatch", i.e., something like obj.getClass().getPointerToMethod("doSomething(int)") in order to determine what concrete method to call. I wrote "something like" as doing this literally is very time-consuming and gets optimized away as much as possible. This dispatch can be moved above the increment and it itself will throw an NPE, if obj happens to be null.
  • When you're lucky, then the dispatch may look like if (obj.getClass() != MyClass.class) uncommon_trap();, which is the simplest case (called "monomorphic call site"), but even this involves dereferencing obj and you again get an NPE for null.

(*) A null check may look like obj.getClass() which in assembler is a single instruction loading from a fixed offset relative to the obj pointer. When obj == null then an unmapped page ist hit.