我目前正在研究一些 Android 性能问题,并注意到 dex 代码中的一些次优模式。我只是想知道是否有人知道这是否是预料之中的,以及其背后的基本原理是什么。
例如,考虑以下 Java 代码:
m_testField += i;
doSomething(m_testField);
构建并通过 baksmali 运行时,它看起来如下所示:
iget v1, p0, Lcom/example/MainActivity$FieldTest;->m_testField:I
add-int/2addr v1, v0
iput v1, p0, Lcom/example/MainActivity$FieldTest;->m_testField:I
iget v1, p0, Lcom/example/MainActivity$FieldTest;->m_testField:I
invoke-direct {p0, v1}, Lcom/example/MainActivity$FieldTest;->doSomething(I)V
与我有关的部分是 iget 操作码,用于将实例字段的值读入寄存器 v1。在前面的操作码中,相同的字段是从完全相同的 v1 寄存器写入的,因此该操作码看起来完全是多余的。
我唯一能想到的是,这样做是为了让这个更加线程安全。但可以肯定的是,这应该是程序员的责任(通过使用同步块(synchronized block))而不是编译器的责任。尽管我不是 100% 确定,但我认为上述行为与大多数 C/C++ 编译器的行为完全不同。
我应该说使用 ProGuard 时生成的 dex 本质上是相同的。我可能还应该提一下,我使用的是最新的 Android 工具和最新型号的 JDK。
最佳答案
对字段的每次访问都是独立的。要获得您描述的行为,您需要添加一个额外的局部变量:
int local = m_testField; // iget
local = local + i;
m_testField = local; // iput
doSomething(local);
也就是说,解释器、即时编译器和提前编译器的某种组合可能最终会在运行时为您进行这些优化。
关于android - Android dex 中的冗余操作码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29089082/