java - 这些 Java 字节偏移量是如何计算的?

标签 java jvm bytecode

我有以下 Java 代码:

public int sign(int a) {
  if(a<0) return -1;
  else if (a>0) return 1;
  else return 0;
}

编译时生成以下字节码:

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

想知道字节偏移量(第一列)是怎么计算出来的,特别是ifgeifle指令的字节数为什么是3字节当所有其他指令都是单字节指令时?

最佳答案

正如评论中已经指出的那样:ifgeifle 指令有一个额外的偏移量。

Java Virtual Machine Instruction Set specification for ifge and ifle此处包含相关提示:

Format

if<cond>
branchbyte1
branchbyte2

这表明有两个附加字节与该指令相关联,即“分支字节”。这些字节组成一个单一的 short 值来确定 offset - 即当条件满足时指令指针应该“跳转”多远。


Edit:

评论让我感到好奇:offset 被定义为 signed 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 处理远跳转指令,包含 四个 分支字节,因此可以覆盖(超过)整个方法范围。

关于java - 这些 Java 字节偏移量是如何计算的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30240139/

相关文章:

java - 为什么可以轻松地重建Java类文件

java - 如何从xslt中的java map 获取数据

java - 单线程与多线程 JMS 生产者

Java:无法访问扩展子类中父类(super class)的 protected 成员

java最大堆大小和操作系统最高输出

java - 寻求有关 JVM 随机崩溃的帮助

java - 什么是 JVM 可以创建的线程的阈值限制数

java - ASM字节码: prepending visitMethodInsn with another invocation

java - 如何将 EOF 发送到 Java 中的进程?

java - 哪些低级任务可以在 JVM 上完成,但不能用 Java 表达?