这些Java字节偏移量如何计算? [英] How are these Java byte offsets calculated?
问题描述
我有以下Java代码:
I have the following Java code:
public int sign(int a) {
if(a<0) return -1;
else if (a>0) return 1;
else return 0;
}
在编译时会生成以下字节码:
which when compiled generated the following bytecode:
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
我想知道字节偏移量计数(第一列)的计算方式,尤其是当所有其他指令均为单字节指令时,为什么ifge
和ifle
指令的字节计数为3个字节?
I want to know how the byte offset count (the first column) is calculated, in particular, why is the byte count for the ifge
and ifle
instructions 3 bytes when all the other instructions are single byte instructions?
推荐答案
正如注释中已经指出的:ifge
和ifle
指令还有一个额外的偏移量.
As already pointed out in the comment: The ifge
and ifle
instructions have an additional offset.
Java虚拟ifge
和ifle
的机器指令集规范在此处包含相关提示:
The Java Virtual Machine Instruction Set specification for ifge
and ifle
contains the relevant hint here:
格式
if<cond>
branchbyte1
branchbyte2
这表示该指令还有两个附加字节,即分支字节".这些字节由一个单独的short
值组成,以确定 offset -即满足条件时指令指针应跳转"多远.
This indicates that there are two additional bytes associated with this instruction, namely the "branch bytes". These bytes are composed to a single short
value to determine the offset - namely, how far the instruction pointer should "jump" when the condition is satisfied.
这些评论让我感到好奇:offset
被定义为一个 signed 16位值,将跳转限制在+/- 32k的范围内.这并不涵盖可能方法的全部范围,该方法根据类文件中的code_length
最多可包含65535个字节.
The comments made me curious: The offset
is defined to be a signed 16 bit value, limiting the jumps to the range of +/- 32k. This does not cover the whole range of a possible method, which may contain up to 65535 bytes according to the code_length
in the class file.
所以我创建了一个测试类,看看会发生什么.此类看起来像这样:
So I created a test class, to see what happens. This class looks like this:
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++
行中的每一行都将转换为由3个字节组成的iinc
指令.所以最终的字节码是
Each of the y++
lines will be translated into a iinc
instruction, consisting of 3 bytes. So the resulting byte code is
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(这是绝对偏移量. relative 偏移量是32766.另请参见此问题)
One can see that it still uses an if_icmpge
instruction, with an offset of 32768 ( It is an absolute offset. The relative offset is 32766. Also see this question)
通过在原始代码中再添加一个y++
,编译后的代码突然变为
By adding a single more y++
in the original code, the compiled code suddenly changes to
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
,并使用
So it reverses the condition from if_icmpge
to if_icmplt
, and handles the far jump with a goto_w
instruction, that contains four branch bytes and can thus cover (more than) a full method range.
这篇关于这些Java字节偏移量如何计算?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!