Why bytecode calls Object->getClass() at a direct field access

只谈情不闲聊 提交于 2019-11-29 16:10:56

I'm assuming lifeTime is a final field that is assigned upon declaration:

 final int lifeTime = 0x0001;

If so, the bytecode is optimized in the following way (it has next to nothing to do with the VM, pure compiler magic):

  • There's no need to really fetch data from memory: all that's needed is to load a constant 1.
  • But what if the owner of the field happens to be null? In this case a NullPointerException must be thrown. To guarantee such behavior compilers emit calls to getClass(), because
    • actually checking for null, constructing a new instance of NullPointerException and throwing it is a lot more byte code,
    • such calls are very optimized in the VM,
    • this method is always available,
    • it takes no arguments.

A simpler example:

class Test {
    private final int myFinalField = 1;

    int test(Test t) {
        return t.myFinalField;
    }
}

If we look at the byte codes of the test() method (JVM this time, but should you translate it to Dalvik, it will be essentially the same), here is a call to getClass() too:

 // access flags 0x0
  test(LTest;)I
   L0
    LINENUMBER 5 L0

    // load t
    ALOAD 1

    // if (t == null) throw new NullPointerException(); compressed in only two instructions
    INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class;
    POP

    // the actual value of myFinalField
    ICONST_1

    IRETURN
   L1
    LOCALVARIABLE this LTest; L0 L1 0
    LOCALVARIABLE t LTest; L0 L1 1
    MAXSTACK = 1
    MAXLOCALS = 2

Andrey's answer gives the specific answer for this question. But here are a couple of notes relevant to this kind of question:

  1. Apparently, you can see a similar thing with bytecodes produced by the Oracle / OpenJDK tool chain. This is not that surprising, since some generation paths for Davlik bytecodes involve compiling Java source code to JVM bytecodes and then translating them to Davlik bytecodes.

  2. If you have come across this odd artifact because you were looking at the bytecodes to get insight into the performance some code, then you are probably looking in the wrong place. In a modern JVM / Davlik / ART engine, the bytecodes are translated into native code, and the native code is what gets executed most or all of the time1.

    To get a more reliable insight into code performance at the "micro" level, you need to examine the native code produced by the AOT or JIT compiler.

  3. One of the reasons that bytecodes emitted by a bytecode compiler are typically not heavily optimized is that doing that could make it more difficult for the AOT / JIT to optimize effectively.

  4. Davlik has been superseded by ART.


1 - With Hotspot JVMs, only JIT and direct bytecode interpretation are supported. Early versions of Davlik were interpret-only, and then JIT support was added, and improved. In ART, all three modes are supported in some form: interpret, JIT and AOT.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!