I decompiled Java (actually Dalvik) bytecode. In the beginning of a method, I access a field of an instance member directly (i.e. not through a getter).
It seems tha Java calls Object.getClass()
on the accessed instance member (mOther
), but doesn't use the result anywhere. Is this some sort of check? Why is this call needed? I suspect it is because I access a field directly (which is defined in that class), but I don't see the connection.
The Java code and the decompiled bytecode are as follows.
(Note that the last instruction loads lifeTime
as constant 0x0001
because in MyOtherClass
, I have lifeTime
as a public final
field, and is currently 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:
It was requested in comments that I provide the method's full source code. Note that mOther
is a final field (for performance reasons). Here you're:
@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; }
}
}
I'm assuming lifeTime is a final field that is assigned upon declaration:
If so, the bytecode is optimized in the following way (it has next to nothing to do with the VM, pure compiler magic):
A simpler example:
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:
Andrey's answer gives the specific answer for this question. But here are a couple of notes relevant to this kind of question:
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.
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.
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.
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.