正如评论中已经指出的那样:ifge 和 ifle 指令有一个额外的偏移量。
Java Virtual Machine Instruction Set specification for ifge and ifle 在此处包含相关提示:
格式
if<cond>
branchbyte1
branchbyte2
这表明有两个附加字节与该指令相关联,即“分支字节”。这些字节组合成单个short 值以确定偏移量 - 即当条件满足时指令指针应该“跳转”多远。
编辑:
cmets 让我感到好奇:offset 被定义为 有符号 16 位值,将跳转限制在 +/- 32k 范围内。这并不涵盖may contain up to 65535 bytes according to the code_length in the class file 的可能方法的全部范围。
所以我创建了一个测试类,看看会发生什么。这个类看起来像这样:
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);
}
}
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
可以看到它仍然使用if_icmpge指令,偏移量为32768(编辑:这是一个绝对偏移量。相对偏移量是32766。另见this question)
通过在原代码中多加一个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
因此它将条件从if_icmpge 反转为if_icmplt,并使用goto_w 指令处理远跳转,该指令包含四个 分支字节,因此可以覆盖(超过)一个完整的方法范围。