问题描述
我最近正在开发一个可以分析 java 类文件的程序。运行程序后,这是它的输出:
class test_1 {
public static String a = "Hello World";
public static void main(String[] args) {
int j = 0;
for(int i = 0;i<10;i++) {
System.out.println(a);
j = j + j*j +j/(j+1);
}
}
}
我得到了一个字节码 0xe2,它没有在 jvm 规范 14 中指定。0xe2 有什么作用??
解决方法
您没有考虑多字节操作码。从 the reference for goto 开始,格式为:
goto
branchbyte1
branchbyte2
无符号字节 branchbyte1 和 branchbyte2 用于构造有符号的 16 位 branchoffset,其中 branchoffset 为 (branchbyte1
goto
是 0xa7,后面应该跟 2 个字节表示分支位置,使指令有 3 个字节宽。您的代码忽略了这一点,反汇编了 1 个字节,然后将接下来的 2 个字节视为有效指令,而它们不是。
你的程序输出每一个字节就好像它们是字节码指令一样,忽略了很多指令都有参数的事实,所以它们是多字节指令。
例如您的程序错误地输出构造函数如下:
2a: aload_0
b7: invokespecial
00: nop
01: aconst_null
b1: return
如果您运行 javap -c test_1.class
,您将看到:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
冒号前的数字是偏移量,不是字节码。如您所见,缺少偏移量 2 和 3,因为 invokespecial
指令使用 2 个字节作为参数,记录如下:
格式
invokespecial
indexbyte1
indexbyte2
说明
无符号的indexbyte1
和indexbyte2
用于构造当前类(§2.6)的运行时常量池的索引,其中索引的值为{{ 1}}。
2个字节为(indexbyte1 << 8) | indexbyte2
和00
,index为1,所以字节码指令如01
所示:javap
如果您随后查看常量池输出,您会看到常量 #1 是 invokespecial #1
无参数构造函数的 methodref
。
您的具体问题与字节码 Object
有关,它不是 3 条指令,而是 goto
的 3 字节指令:
格式
a7 ff e2
说明
无符号字节 goto
branchbyte1
branchbyte2
和 branchbyte1
用于构造有符号的 16 位分支偏移,其中分支偏移为 branchbyte2
。
表示(branchbyte1 << 8) | branchbyte2
是ff e2
,表示代替
branchoffset = 0xffe2 = -30
您的程序应该打印如下内容:
a7: goto
ff: impdep2
e2: (null)