gpt4 book ai didi

java - 为什么字节码在直接字段访问时调用 Object->getClass()

转载 作者:搜寻专家 更新时间:2023-11-01 08:07:31 27 4
gpt4 key购买 nike

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

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

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

(请注意,最后一条指令将 lifeTime 加载为常量 0x0001 因为在 MyOtherClass 中,我将 lifeTime 作为一个 public 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; }
}
}

最佳答案

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

 final int lifeTime = 0x0001;

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

  • 不需要真正从内存中获取数据:所有需要的只是加载一个常量 1。
  • 但是,如果该字段的所有者恰好是 null 怎么办?在这种情况下,必须抛出 NullPointerException。为了保证这种行为,编译器发出对 getClass() 的调用,因为
    • 实际上检查 null,构造 NullPointerException 的新实例并抛出它是很多更多的字节代码,
    • 此类调用在 VM 中得到了非常优化,
    • 此方法始终可用,
    • 它不需要参数。

一个更简单的例子:

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

关于java - 为什么字节码在直接字段访问时调用 Object->getClass(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13115812/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com