Java 字节码初学者问题:"istore" 变量使用顺序,"byte usage" 等
Java bytecode beginner questions: order of "istore" variable usage, "byte usage" etc
我有以下 Java 代码:
public static void fun(int[] a) {
int min;
for(int j=0;j<a.length-1;j++) {
min=j;
for(int i=j+1;i<a.length;i++)
if(a[i]<a[min]) min=i;
if(min!=j) {
int temp = a[j];
a[j] = a[min];
a[min]=temp;
}
}
}
我在上述代码生成的 class 文件上使用了 javap -c
来打印上述程序的 Java 字节码,我得到以下输出:
0: iconst_0
1: istore_2
2: iload_2
3: aload_0
4: arraylength
5: iconst_1
6: isub
7: if_icmpge 64
10: iload_2
11: istore_1
12: iload_2
13: iconst_1
14: iadd
15: istore_3
16: iload_3
17: aload_0
18: arraylength
19: if_icmpge 39
22: aload_0
23: iload_3
24: iaload
25: aload_0
26: iload_1
27: iaload
28: if_icmpge 33
31: iload_3
32: istore_1
33: iinc 3, 1
36: goto 16
39: iload_1
40: iload_2
41: if_icmpeq 58
44: aload_0
45: iload_2
46: iaload
47: istore_3
48: aload_0
49: iload_2
50: aload_0
51: iload_1
52: iaload
53: iastore
54: aload_0
55: iload_1
56: iload_3
57: iastore
58: iinc 2, 1
61: goto 2
64: return
我的问题主要与 istore
命令有关。可以看到,在“1:”处,生成的字节码是istore_2
。为什么在这种情况下使用 istore_2
,而不是 istore_0
或 istore_1
?正如我们所看到的,稍后,在“11:”处,使用了 istore_1
,但是没有使用 istore_0
。这有什么原因吗,为什么一开始用变量2,后面用变量1?
抱歉,如果上面的措辞不好,我现在才开始在大学学习Java字节码。
此外,我正试图从我们必须学习使用的列表中找出哪些命令使用超过一个字节。这是列表,有人可以验证下面命令中的 "byte usage"(我不确定正确的术语)是否正确(我们必须在考试中手动转换,使用下面给出的命令列表和我需要了解每个命令使用多少字节 - 它在我们的讲座中解释得非常糟糕和快速,并且没有来自讲师的在线注释)。
"aload_n" - 1 byte
"iconst_n" - 1 byte
"iaload"- 1 byte
"istore_n"- 1 byte
"arraylength"- 1 byte
"if_icmpge"- 3 bytes
"if_icmple"- 3 bytes
"iinc i c" - unsure? 3 bytes?
"goto n" - 3 bytes?
"ireturn" - 1 byte
最后,我认为 goto n
使用 3 个字节(如上所述),但是,有人可以解释为什么会这样吗?
非常感谢您的帮助,我知道这是一个很长的问题!
劳伦
关于字节的使用,可以参考specification。引用开头的注释:“指令格式图中的每个单元格代表一个8位字节”所以应该很容易得到每条指令的大小。
As you can see, at "1:", the bytecode generated is "istore_2". Why, in this case, is "istore_2" used, rather than "istore_0" or "istore_1"?
只是因为在第 1 行,字节码将零值存储到局部变量 j
中,该变量恰好位于索引 2 处。类似地,istore_1
将值存储在操作数堆栈中以索引 1 处的局部变量(在本例中为 min
)。
这是有效的,因为长话短说,每个方法都分配了一个堆栈帧,其中包含 local variables,可通过分配在 compile-time 的索引访问。
对于goto
指令,这也是在specification:
goto
branchbyte1
branchbyte2
The unsigned bytes branchbyte1 and branchbyte2 are used to construct a signed 16-bit branchoffset, where branchoffset is (branchbyte1 << 8) | branchbyte2. Execution proceeds at that offset from the address of the opcode of this goto instruction. The target address must be that of an opcode of an instruction within the method that contains this goto instruction.
操作码后有两个字节,组成16位偏移量。生成的目标地址计算为:
target_address = address_of_current_goto + 16_bit_offset
我有以下 Java 代码:
public static void fun(int[] a) {
int min;
for(int j=0;j<a.length-1;j++) {
min=j;
for(int i=j+1;i<a.length;i++)
if(a[i]<a[min]) min=i;
if(min!=j) {
int temp = a[j];
a[j] = a[min];
a[min]=temp;
}
}
}
我在上述代码生成的 class 文件上使用了 javap -c
来打印上述程序的 Java 字节码,我得到以下输出:
0: iconst_0
1: istore_2
2: iload_2
3: aload_0
4: arraylength
5: iconst_1
6: isub
7: if_icmpge 64
10: iload_2
11: istore_1
12: iload_2
13: iconst_1
14: iadd
15: istore_3
16: iload_3
17: aload_0
18: arraylength
19: if_icmpge 39
22: aload_0
23: iload_3
24: iaload
25: aload_0
26: iload_1
27: iaload
28: if_icmpge 33
31: iload_3
32: istore_1
33: iinc 3, 1
36: goto 16
39: iload_1
40: iload_2
41: if_icmpeq 58
44: aload_0
45: iload_2
46: iaload
47: istore_3
48: aload_0
49: iload_2
50: aload_0
51: iload_1
52: iaload
53: iastore
54: aload_0
55: iload_1
56: iload_3
57: iastore
58: iinc 2, 1
61: goto 2
64: return
我的问题主要与 istore
命令有关。可以看到,在“1:”处,生成的字节码是istore_2
。为什么在这种情况下使用 istore_2
,而不是 istore_0
或 istore_1
?正如我们所看到的,稍后,在“11:”处,使用了 istore_1
,但是没有使用 istore_0
。这有什么原因吗,为什么一开始用变量2,后面用变量1?
抱歉,如果上面的措辞不好,我现在才开始在大学学习Java字节码。
此外,我正试图从我们必须学习使用的列表中找出哪些命令使用超过一个字节。这是列表,有人可以验证下面命令中的 "byte usage"(我不确定正确的术语)是否正确(我们必须在考试中手动转换,使用下面给出的命令列表和我需要了解每个命令使用多少字节 - 它在我们的讲座中解释得非常糟糕和快速,并且没有来自讲师的在线注释)。
"aload_n" - 1 byte
"iconst_n" - 1 byte
"iaload"- 1 byte
"istore_n"- 1 byte
"arraylength"- 1 byte
"if_icmpge"- 3 bytes
"if_icmple"- 3 bytes
"iinc i c" - unsure? 3 bytes?
"goto n" - 3 bytes?
"ireturn" - 1 byte
最后,我认为 goto n
使用 3 个字节(如上所述),但是,有人可以解释为什么会这样吗?
非常感谢您的帮助,我知道这是一个很长的问题! 劳伦
关于字节的使用,可以参考specification。引用开头的注释:“指令格式图中的每个单元格代表一个8位字节”所以应该很容易得到每条指令的大小。
As you can see, at "1:", the bytecode generated is "istore_2". Why, in this case, is "istore_2" used, rather than "istore_0" or "istore_1"?
只是因为在第 1 行,字节码将零值存储到局部变量 j
中,该变量恰好位于索引 2 处。类似地,istore_1
将值存储在操作数堆栈中以索引 1 处的局部变量(在本例中为 min
)。
这是有效的,因为长话短说,每个方法都分配了一个堆栈帧,其中包含 local variables,可通过分配在 compile-time 的索引访问。
对于goto
指令,这也是在specification:
goto
branchbyte1
branchbyte2
The unsigned bytes branchbyte1 and branchbyte2 are used to construct a signed 16-bit branchoffset, where branchoffset is (branchbyte1 << 8) | branchbyte2. Execution proceeds at that offset from the address of the opcode of this goto instruction. The target address must be that of an opcode of an instruction within the method that contains this goto instruction.
操作码后有两个字节,组成16位偏移量。生成的目标地址计算为:
target_address = address_of_current_goto + 16_bit_offset