我反编译了Java(实际上是Dalvik)字节码。在方法的开头,我直接访问实例成员的字段(即不通过getter)。
似乎Java在访问的实例成员(Object.getClass()
)上调用mOther
,但不在任何地方使用结果。这是某种检查吗?为什么需要这个电话?我怀疑是因为我直接访问了一个字段(在该类中定义了),但我看不到连接。
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; }
}
}
答案 0 :(得分:4)
我假设lifeTime是在声明时分配的最终字段:
final int lifeTime = 0x0001;
如果是这样,字节码会以下列方式进行优化(它与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
答案 1 :(得分:0)
安德烈的回答给出了这个问题的具体答案。但这里有几个与此类问题相关的注释:
显然,您可以看到Oracle / OpenJDK工具链生成的字节码类似的东西。这并不奇怪,因为Davlik字节码的一些生成路径涉及将Java源代码编译为JVM字节码,然后将它们转换为Davlik字节码。
如果您遇到这个奇怪的工件,因为您正在查看字节码以深入了解某些代码的性能,那么您可能正在查找错误的位置。在现代JVM / Davlik / ART引擎中,字节码被转换为本机代码,本机代码是大部分或全部时间执行的 1 。
为了更加可靠地了解“微观”级别的代码性能,您需要检查AOT或JIT编译器生成的本机代码。
字节码编译器发出的字节码通常没有经过大量优化的原因之一是,这样做可能会使AOT / JIT更难以有效优化。
Davlik已被ART取代。
1 - 使用Hotspot JVM,仅支持JIT和直接字节码解释。早期版本的Davlik只是解释,然后JIT支持被添加和改进。在ART中,所有三种模式都以某种形式支持:interpre,JIT和AOT。