Why bytecode calls Object-> getClass () when directly accessing a field

I decompiled Java bytecode (actually Dalvik). At the beginning of the method, I access the field of the instance element directly (i.e. Not via getter).

Java seems to call Object.getClass() on an available instance member ( mOther ), but does not use the result anywhere. Is this some kind of check? Why do you need this call? I suspect this is because I directly access the field (which is defined in this class), but I do not see it.

The Java code and decompiled bytecode are as follows.

(Note that the last command loads lifeTime as a constant 0x0001 , because in MyOtherClass I have lifeTime as a public final field and is being initialized from code.)

 MyOtherClass other = mOther; if (mAge >= other.lifeTime) { // lifeTime is initialized to 0x0001 end(); return; } .line 53 move-object/from16 v0, p0 iget-object v0, v0, Lcom/example/engine/MyClass1;->mOther:Lcom/example/engine/MyOtherClass; move-object/from16 v16, v0 .line 54 .local v16, other:Lcom/example/engine/MyOtherClass; move-object/from16 v0, p0 iget v0, v0, Lcom/example/engine/MyClass1;->mAge:I move/from16 v18, v0 // Why is Object->getClass() called? invoke-virtual/range {v16 .. v16}, Ljava/lang/Object;->getClass()Ljava/lang/Class; const/16 v19, 0x0001 

UPDATE:

In the comments, it was requested that I provide the complete method code. Note that mOther is the final field (for performance reasons). Here you:

 @Override public void doStep() { MyOtherClass other = mOther; if (mAge >= other.lifeTime) { end(); return; } mAge += TICK_TIME; boolean isSurrounded = false; if (mAge > mLastSurroundTime + other.surroundingTime) { int distance = (int)other.maxSurroundDistance; for (int bx = bx0; bx <= bx1; ++bx) { if (bx < 0 || bx >= mSize) { continue; } for (int by = by0; by <= by1; ++by) { if (by < 0 || by >= mSize) { continue; } ArrayList<WorldObject> candidates = getCandidatesAtPos(bx, by); for (int i = 0; i < candidates.size(); ++i) { WorldObject obj = candidates.get(i); if (mSelf!= obj && mSelf.getDistanceFrom(obj) <= other.maxSurroundDistance) { obj.notifyDangerImminent(mSelf); isSurrounded = true; } } } } if (isSurrounded) { mLastSurroundTime = mAge; } } } 
+2
source share
2 answers

I assume that lifeTime is the final field that is assigned to the declaration:

  final int lifeTime = 0x0001; 

If so, the bytecode is optimized as follows (it has almost nothing to do with a virtual machine, pure compiler magic):

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

The simplest example:

 class Test { private final int myFinalField = 1; int test(Test t) { return t.myFinalField; } } 

If we look at the bytecodes of the test () method (the JVM this time, but if you translate it to Dalvik, it will be essentially the same), here's the 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 
+4
source
The answer to the answer gives a concrete answer to this question. But here are a few notes related to this issue:
  • Apparently you can see a similar thing with bytecodes generated by the Oracle / OpenJDK toolchain. This is not surprising since some of the generation paths for Davlik bytecodes include compiling Java source code into JVM bytecodes and then translating them into Davlik bytecodes.

  • If you came across this odd artifact because you looked at bytecodes to get an idea of ​​the performance of some code, then you are probably looking in the wrong place. In the modern JVM / Davlik / ART engine, bytecodes are converted to native code, and native code is what runs most or all of the time 1 .

    To get a more reliable idea of ​​micro-level code performance, you need to study your own code created by the AOT or JIT compiler.

  • One reason that the bytecodes released by the bytecode compiler is usually not very optimized is that doing this can make it difficult to optimize AOT / JIT.

  • Davlik was replaced by ART.


1 - with JVM Hotspot only JIT and direct interpretation of bytecode are supported. Early versions of Davlik were interpretable, and then JIT support was added and improved. In ART, all three modes are supported in one form or another: interpretation, JIT, and AOT.

0
source

Source: https://habr.com/ru/post/1272972/


All Articles