Java中{for`循环指令的意外排序

时间:2017-06-10 17:11:02

标签: java for-loop

考虑以下非常简单的循环,它有点间隔:

    /* 01 */  System.out.println(10);
    /* 02 */  for(
    /* 03 */        int i = 0; 
    /* 04 */        i < 100; 
    /* 05 */        i++)
    /* 06 */    System.out.println(i);

第1行并不重要。只有在循环本身之前才为Eclipse调试器创建一个起始点。

当我在调试器中运行我的代码时,我希望看到以下行的运行顺序:

1,3,4,接着是重复的6,5,4,(6,5,4,6,5,4,6,5,4 ......)系列

相反,我得到的是

1,3,4,然后是重复序列 2 ,6,5,4,( 2 ,6,5,4, 2 < / strong>,6,5,4 ......)

显然,这没什么实际意义 - 循环完全按预期运行。但是我计划为我的类创建一个新概念的演示,而这个额外的执行步骤使我提出的(有点困难)代码在调试器中更难以遵循。

我希望能够通过向他们解释正在发生的事情来抵消一些混乱。为什么第2行被重新表示为一个步骤?那一刻发生了什么?

1 个答案:

答案 0 :(得分:4)

这是因为Java字节码具有用于比较+跳转的单个融合指令,并且一条指令是可以具有行号或可以逐步通过的最小量子。

您正在使用的Eclipse ECJ似乎强调跳转,并将compare + jump作为for循环本身的一部分进行注释。这导致在每次比较/跳跃期间02停止。

OpenJDK似乎强调“比较”方面,并将其注释为for循环条件的一部分。这会给出您期望的结果,而不会停留在02

两者都没有直接错,但我也倾向于赞成你的解释。也许你可以说服Eclipse使用OpenJDK进行这个特定的演示,或者使用不同的Java调试器?

下面是反汇编的字节码+调试信息。欧洲法院将条件放在最底层:

  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #16                 // Field java/lang/System.out:Ljava/io/PrintStream;
       3: bipush        10
       5: invokevirtual #22                 // Method java/io/PrintStream.println:(I)V
       8: iconst_0
       9: istore_1
      10: goto          23
      13: getstatic     #16                 // Field java/lang/System.out:Ljava/io/PrintStream;
      16: iload_1
      17: invokevirtual #22                 // Method java/io/PrintStream.println:(I)V
      20: iinc          1, 1
      23: iload_1
      24: bipush        100
      26: if_icmplt     13               // HERE
      29: return
    LineNumberTable:
      line 3: 0
      line 5: 8
      line 6: 10
      line 8: 13
      line 7: 20
      line 6: 23
      line 4: 26                         /* 02 */ in your code
      line 9: 29

OpenJDK提供更直接的翻译,需要一个倒置条件,其中没有使用for(行注释指令:

  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: bipush        10
       5: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
       8: iconst_0
       9: istore_1
      10: iload_1
      11: bipush        100
      13: if_icmpge     29
      16: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      19: iload_1
      20: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
      23: iinc          1, 1
      26: goto          10
      29: return
    LineNumberTable:
      line 3: 0
      line 5: 8
      line 6: 10
      line 8: 16
      line 7: 23
      line 9: 29