我有以下Java代码:
public int sign(int a) {
if(a<0) return -1;
else if (a>0) return 1;
else return 0;
}
Run Code Online (Sandbox Code Playgroud)
在编译时生成以下字节码:
public int sign(int);
Code:
0: iload_1
1: ifge 6
4: iconst_m1
5: ireturn
6: iload_1
7: ifle 12
10: iconst_1
11: ireturn
12: iconst_0
13: ireturn
Run Code Online (Sandbox Code Playgroud)
我想知道如何计算字节偏移计数(第一列),特别是当所有其他指令都是单字节指令时,为什么字节计数ifge和ifle指令3字节?
正如评论中已经指出的那样:ifge和ifle指令有一个额外的偏移量.
在为Java虚拟机指令集规范ifge,并ifle包含了相关的提示在这里:
格式
Run Code Online (Sandbox Code Playgroud)if<cond> branchbyte1 branchbyte2
这表示该指令有两个附加字节,即"分支字节".这些字节由单个short值组成,以确定偏移量 - 即,当条件满足时,指令指针应该"跳转"多远.
编辑:
这些评论让我很好奇:它offset被定义为一个带符号的 16位值,将跳跃限制在+/- 32k的范围内.这不包括可能方法的整个范围,根据code_length类文件中的方法,该方法最多可包含65535个字节.
所以我创建了一个测试类,看看会发生什么.这个类看起来像这样:
class FarJump
{
public static void main(String args[])
{
call(0, 1);
}
public static void call(int x, int y)
{
if (x < y)
{
y++;
y++;
... (10921 times) ...
y++;
y++;
}
System.out.println(y);
}
}
Run Code Online (Sandbox Code Playgroud)
每y++行将被转换为一个iinc由3个字节组成的指令.所以得到的字节码是
public static void call(int, int);
Code:
0: iload_0
1: iload_1
2: if_icmpge 32768
5: iinc 1, 1
8: iinc 1, 1
...(10921 times) ...
32762: iinc 1, 1
32765: iinc 1, 1
32768: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
32771: iload_1
32772: invokevirtual #4 // Method java/io/PrintStream.println:(I)V
32775: return
Run Code Online (Sandbox Code Playgroud)
可以看到它仍然使用一条if_icmpge指令,偏移量为32768(编辑:它是绝对偏移量.相对偏移量是32766.另请参阅此问题)
通过y++在原始代码中添加一个,编译后的代码突然变为
public static void call(int, int);
Code:
0: iload_0
1: iload_1
2: if_icmplt 10
5: goto_w 32781
10: iinc 1, 1
13: iinc 1, 1
....
32770: iinc 1, 1
32773: iinc 1, 1
32776: goto_w 32781
32781: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
32784: iload_1
32785: invokevirtual #4 // Method java/io/PrintStream.println:(I)V
32788: return
Run Code Online (Sandbox Code Playgroud)
因此它将条件从if_icmpgeto转换为if_icmplt,并使用goto_w包含四个分支字节的指令处理远跳,因此可以覆盖(超过)完整的方法范围.
| 归档时间: |
|
| 查看次数: |
794 次 |
| 最近记录: |