7

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

4

3 回答 3

8

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

例如,如果您编写x *= 3; x *= 4;,您可能会得到如下行的字节码

iload_1
iconst_3
imul
istore_1
iload_1
iconst_4
imul
istore_1

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

于 2017-03-14T19:11:15.510 回答
6

查看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因为时间点。

有趣的是,在这个例子中如果你全部删除store并且load程序的行为不会改变。但是,Java 编译器通常不会尝试变得聪明。它或多或少地直接编译 Java 代码。在这种情况下,局部变量i直接对应于局部变量 1 和 2。

有关更多信息,请参阅Java 编译器优化。

于 2017-03-14T09:05:36.353 回答
1

看,JVM 中的每个操作都是在操作数堆栈上完成的。因此,每当您必须对变量执行任何操作时,您必须首先通过加载命令加载(推送)操作数堆栈,然后执行操作。

这就是为什么存储后跟字节码中的加载指令的原因。

于 2018-07-13T08:29:19.557 回答