根据执行历史记录,给定指令的操作数堆栈大小是否会有所不同?
Can operand stack size differ at given instruction depending on execution history?
例如,对于方法
public int f() {
int k = 1;
for (int i = 0; i < 10; i++) {
k += 2;
}
return k;
}
javac
生成以下字节码:
public int f();
Code:
0: iconst_1
1: istore_1
2: iconst_0
3: istore_2
4: iload_2
5: bipush 10
7: if_icmpge 19
10: iinc 1, 2
13: iinc 2, 1
16: goto 4
19: iload_1
20: ireturn
在标签 4 处,堆栈具有相同的大小 (0),无论之前是哪条指令:3 或 16。
从 java 代码 生成的字节码 通常是这样吗?
是的。字节码验证强制堆栈大小在任何给定指令处保持不变,而不管控制流如何。 (它还强制类型兼容)。因此,这不仅适用于从编译的 Java 派生的字节码,而且适用于所有有效的字节码。
要参考 Antimony 的回答,请参阅
JVM specification §4.9.2. Structural Constraints(谢谢霍尔格!):
- ...
- If an instruction can be executed along several different execution paths, the operand stack must have the same depth (§2.6.2) prior to the execution of the instruction, regardless of the path taken.
在这个答案的第一个版本中,我引用了 §4.10.2.1. The Process of Verification by Type Inference,这是针对 class 不包含 StackMapTable
属性的文件(版本号为 49.0 或以下):
... The verifier ensures that at any given point in the program, no matter what code path is taken to reach that point, all of the following are true:
- The operand stack is always the same size and contains the same types of values.
- ...
例如,对于方法
public int f() {
int k = 1;
for (int i = 0; i < 10; i++) {
k += 2;
}
return k;
}
javac
生成以下字节码:
public int f();
Code:
0: iconst_1
1: istore_1
2: iconst_0
3: istore_2
4: iload_2
5: bipush 10
7: if_icmpge 19
10: iinc 1, 2
13: iinc 2, 1
16: goto 4
19: iload_1
20: ireturn
在标签 4 处,堆栈具有相同的大小 (0),无论之前是哪条指令:3 或 16。
从 java 代码 生成的字节码 通常是这样吗?
是的。字节码验证强制堆栈大小在任何给定指令处保持不变,而不管控制流如何。 (它还强制类型兼容)。因此,这不仅适用于从编译的 Java 派生的字节码,而且适用于所有有效的字节码。
要参考 Antimony 的回答,请参阅 JVM specification §4.9.2. Structural Constraints(谢谢霍尔格!):
- ...
- If an instruction can be executed along several different execution paths, the operand stack must have the same depth (§2.6.2) prior to the execution of the instruction, regardless of the path taken.
在这个答案的第一个版本中,我引用了 §4.10.2.1. The Process of Verification by Type Inference,这是针对 class 不包含 StackMapTable
属性的文件(版本号为 49.0 或以下):
... The verifier ensures that at any given point in the program, no matter what code path is taken to reach that point, all of the following are true:
- The operand stack is always the same size and contains the same types of values.
- ...