为什么字节码在直接字段访问时调用Object-> getClass() [英] Why bytecode calls Object->getClass() at a direct field access

查看:133
本文介绍了为什么字节码在直接字段访问时调用Object-> getClass()的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我反编译了Java(实际上是Dalvik)字节码。在方法的开头,我直接访问实例成员的字段(即不通过getter)。

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).

似乎Java调用访问的实例成员上的Object.getClass() mOther ),但不在任何地方使用结果。这是某种检查吗?为什么需要这个电话?我怀疑这是因为我直接访问一个字段(在该类中定义),但我没有看到连接。

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.

Java代码和反编译的字节码如下。

The Java code and the decompiled bytecode are as follows.

(注意,最后一条指令加载 lifeTime 为常量 0x0001 因为在 MyOtherClass 中,我有 lifeTime 作为公共最终字段,目前已从代码初始化。)

(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



更新:



我在评论中要求提供方法的完整源代码。请注意, mOther 是最终字段(出于性能原因)。你在这里:

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; }
    }
}


推荐答案

我假设lifeTime是一个在声明时分配的最终字段:

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

 final int lifeTime = 0x0001;

如果是这样,字节码按以下方式优化(它几乎与VM,纯编译器魔术):

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


  • 无需从内存中真正获取数据:所需的只是加载常量1. / li>
  • 但如果该字段的所有者恰好是 null 怎么办?在这种情况下,必须抛出NullPointerException。为了保证这种行为,编译器会调用getClass(),因为

    • 实际检查null,构造一个新的NullPointerException实例并抛出它很多更多字节代码,

    • 此类调用在VM中进行了优化,

    • 此方法始终可用,

    • 它没有参数。

    • 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.

      一个更简单的例子:

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

      如果我们查看测试的字节代码( )方法(JVM这次,但你应该把它翻译成Dalvik,它基本上是一样的),这里也是对getClass()的调用:

      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
      

      这篇关于为什么字节码在直接字段访问时调用Object-&gt; getClass()的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆