Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
211 views
in Technique[技术] by (71.8m points)

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

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; }
    }
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...