为什么java字节码“存储”通常后跟“加载”?

时间:2017-03-14 07:11:15

标签: java bytecode jvm-bytecode

当我从一些小的java函数中读取jvm字节码时,我发现当一个新的局部变量在操作数堆栈上计算时,假设它将存储在局部变量表中,但通常它会被加载到操作数堆栈立即(字面上只是字节码)。我操作不好,是不必要的操作?

3 个答案:

答案 0 :(得分:7)

Java编译器倾向于以非常简单和直接的方式编译事物,将优化留给JIT。

例如,如果你写x *= 3; x *= 4;,你可能会得到字节码

iload_1
iconst_3
imul
istore_1
iload_1
iconst_4
imul
istore_1

编译器理论上可以判断出存储/加载对是多余的并将其删除。但是有几个理由不这样做 - 1)这增加了很多复杂性而没有任何好处,因为JIT无论如何都会优化一切2)它使调试变得更难,因为你不再能够访问所有局部变量的值3)如果在此表达式的中间以某种方式抛出异常,则局部变量将具有不正确的值。

答案 1 :(得分:5)

查看dspin字节码

Method void dspin()
0   dconst_0       // Push double constant 0.0
1   dstore_1       // Store into local variables 1 and 2
2   goto 9         // First time through don't increment
5   dload_1        // Push local variables 1 and 2 
6   dconst_1       // Push double constant 1.0 
7   dadd           // Add; there is no dinc instruction
8   dstore_1       // Store result in local variables 1 and 2
9   dload_1        // Push local variables 1 and 2 
10  ldc2_w #4      // Push double constant 100.0 
13  dcmpg          // There is no if_dcmplt instruction
14  iflt 5         // Compare and loop if less than (i < 100.0)
17  return         // Return void when done

load后面的唯一store位于偏移9.您可以看到偏移9可以通过两条不同的路径到达:(1)偏移2与goto 9; (2)从偏移8开始依次进行

dload_1将局部变量1和2的值推送到操作数堆栈(两个变量因为double):在第一次尝试进入循环时的情况(1)中,在情况(2)中尝试在稍后的时间点进入循环。

有趣的是,在此示例中,如果删除所有storeload,程序的行为将不会更改。但是,Java编译器通常不会尝试变聪明。它或多或少地直接编译Java代码。在这种情况下,局部变量i直接对应于局部变量1和2.

有关详细信息,请参阅Optimization by Java Compiler

答案 2 :(得分:0)

请参见,JVM中的每个操作都是在操作数堆栈上完成的。因此,无论何时必须对变量执行任何操作,都必须首先通过 load 命令将数据加载(推入)操作数堆栈,然后执行操作。

这就是为什么 store 后跟字节码中的 load 指令的原因。