为什么在字节码的直接字段访问调用对象 - >的getClass()(Why bytecode

2019-07-03 10:00发布

我反编译的Java(实际上的Dalvik)字节码。 在方法的开始,我直接访问一个实例成员的一个字段(即,不通过的吸气剂)。

看来THA Java调用Object.getClass()在访问实例成员( mOther ),但并没有在任何地方使用的结果。 这是某种形式的检查? 这是为什么呢呼叫所需? 我怀疑这是因为我直接访问场(这是在类中定义的),但我没有看到连接。

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

(请注意,最后一条指令的负载lifeTime为常数0x0001 ,因为在MyOtherClass ,我有lifeTimepublic final场,目前从代码初始化)。

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是最后一个字段(由于性能原因)。 在这里,您是:

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

Answer 1:

我假设寿命是最后一个字段是在声明中分配:

 final int lifeTime = 0x0001;

如果是这样,字节码是通过以下方式优化(它旁边无关的VM,纯编译魔术):

  • 有没有必要真正从内存中获取数据:所有需要是加载一个常数1。
  • 但是,如果场的老板发生了什么是 ? 在这种情况下,一个NullPointerException必须抛出。 为了保证这种行为编译器发出来的getClass()调用,因为
    • 其实检查空,构建NullPointerException异常的一个新实例,并把它扔是一个很大的字节码,
    • 这样的电话在虚拟机非常优化,
    • 这种方法始终可用,
    • 它不带任何参数。

一个简单的例子:

class Test {
    private final int myFinalField = 1;

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

如果我们看一下test()方法中的字节码(JVM这一次,但你应该把它转换成Dalvik的,这将是基本相同),这里是的getClass(通话)太:

 // 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


Answer 2:

安德烈的回答给出了这一问题的具体答案。 但这里有一对夫妇的有关这类问题的注意事项:

  1. 显然,你可以看到由Oracle / OpenJDK的工具链生成的字节码类似的事情。 这并不奇怪,因为对于Davlik一些字节码生成路径涉及编译Java源代码,JVM字节码,然后翻译他们Davlik字节码。

  2. 如果您所遇到的这个奇怪的神器,因为你看字节码获取洞察性能的一些代码,那么你可能找错了地方。 在现代JVM / Davlik / ART引擎,字节码转换为本地代码,和本机代码是什么被执行大部分或全部的时间1。

    要在“微观”层面获得更可靠的洞察代码的性能,你需要检查由AOT或JIT编译器生成的本地代码。

  3. 其中一个是字节码由字节码编译发出的原因通常不是高度优化的是,这样做可以使它更加困难AOT / JIT有效优化。

  4. Davlik已被ART取代。


1 -随着热点的JVM,只有JIT和直接字节码解释的支持。 Davlik的早期版本中,解释只,然后加入JIT的支持,并改进。 在艺术上,所有三种模式都支持某种形式:解释,JIT和AOT。



文章来源: Why bytecode calls Object->getClass() at a direct field access