在写一个回答有关 JVM 字节码偏移量的问题 https://stackoverflow.com/a/30240357/3182664,我注意到 javac 的行为和生成的类文件中有一些我无法解释的内容:
当编译这样的类时
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 - too much code to post here!)
y++;
y++;
}
System.out.println(y);
}
}
那么生成的字节码将包含以下内容if_icmpge https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.if_icmp_cond操作说明:
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
...
根据跳转指令的文档,偏移量(在本例中为 32768)计算如下:
如果比较成功,则无符号分支字节1 and 分支字节2用于构造有符号的 16 位偏移量,其中偏移量计算为(branchbyte1 .
所以偏移量被称为signed16 位值。然而,最大值signed16位值可以容纳的是32767,而不是32768。
生成的类文件看起来仍然有效,并且可以正常执行。
我看了一下OpenJDK 中的字节码检查 http://hg.openjdk.java.net/jdk8u/jdk8u-dev/jdk/file/59deb2d00b29/src/share/native/common/check_code.c#l1160,(对我来说)这似乎只是由于括号放错了位置而有效:
int jump = (((signed char)(code[offset+1])) << 8) + code[offset+2];
它将第一个字节转换为signed char
. Then它将应用移位,并添加第二个字节。我本来希望它是
int jump = (((signed char)(code[offset+1]) << 8)) + code[offset+2];
或者甚至可能
int jump = (signed char)((code[offset+1]) << 8) + code[offset+2]);
但我不熟悉类型提升以及转换有符号和无符号类型的可能的特定于编译器的警告,所以我不确定这个转换背后是否有更深层次的含义......
那么32768的跳转偏移量是否符合规范呢? OpenJDK 中的跳转计算代码在这方面有什么意义吗?