【问题标题】:Erroneous for-loops in Java?Java中错误的for循环?
【发布时间】:2025-12-24 08:20:09
【问题描述】:

我观察到运行以下 java 代码的错误行为:

public class Prototype {
  public static void main(String[] args) {
    final int start = Integer.MAX_VALUE/2;
    final int end = Integer.MAX_VALUE;
    {
      long b = 0;
      for (int i = start; i < end; i++) {
        b++;
      }
      System.out.println(b);
    }
    {
      long b = 0;
      for (int i = start; i < end; i++) {
        b++;
      }
      System.out.println(b);
    }
  }
}

两个循环的作用完全相同。然而,第二个输出一个不确定的错误值。我正在使用版本在 Linux 上运行代码:

java version "1.6.0_25"
Java(TM) SE Runtime Environment (build 1.6.0_25-b06)
Java HotSpot(TM) 64-Bit Server VM (build 20.0-b11, mixed mode)

样本输出:

1073741811
141312

你能重现它吗?是bug吗?

编辑:奇怪

final int end = Integer.MAX_VALUE - 1;

工作正常。

【问题讨论】:

  • 一定有另一个错误,因为我在两个循环中都得到了值 1073741824
  • 我在运行 OS X 10.6.8 的 MacBook 上重现了这个错误。
  • 运行 #1:1073741811 76800 运行 #2:1073741811 77824 运行 #3:1073741811 60416 运行 #4:1073741811 53248 在 Windows 7 64 位上使用 jdk1.6.0_25
  • 1.6.0.24 on Windows 7,只是想指出即使第一行似乎也不正确
  • 在 MAC 上转载。值为 1073741811 和 97280

标签: java for-loop non-deterministic


【解决方案1】:

根据 Java 专家的说法,这是 bug 5091921,已在 JDK7 中修复,但未计划在 JDK6 中修复。

【讨论】:

    【解决方案2】:

    我可以使用 Eclipse 生成的 .class 文件来重现它,但在使用 javac 在命令行上编译时不能。

    生成的字节码不同:

    javac 输出

    public static void main(java.lang.String[]);
      Code:
       0:   lconst_0
       1:   lstore_3
       2:   ldc #2; //int 1073741823
       4:   istore  5
       6:   iload   5
       8:   ldc #3; //int 2147483647
       10:  if_icmpge   23
       13:  lload_3
       14:  lconst_1
       15:  ladd
       16:  lstore_3
       17:  iinc    5, 1
       20:  goto    6
       23:  getstatic   #4; //Field java/lang/System.out:Ljava/io/PrintStream;
       26:  lload_3
       27:  invokevirtual   #5; //Method java/io/PrintStream.println:(J)V
       30:  lconst_0
       31:  lstore_3
       32:  ldc #2; //int 1073741823
       34:  istore  5
       36:  iload   5
       38:  ldc #3; //int 2147483647
       40:  if_icmpge   53
       43:  lload_3
       44:  lconst_1
       45:  ladd
       46:  lstore_3
       47:  iinc    5, 1
       50:  goto    36
       53:  getstatic   #4; //Field java/lang/System.out:Ljava/io/PrintStream;
       56:  lload_3
       57:  invokevirtual   #5; //Method java/io/PrintStream.println:(J)V
       60:  return
    

    为了便于阅读,这里是由Soot 生成的 Grimp 输出:

        java.lang.String[] r0;
        long l0, l2;
        int i1, i3;
    
        r0 := @parameter0;
        l0 = 0L;
        i1 = 1073741823;
    
     label0:
        if i1 >= 2147483647 goto label1;
    
        l0 = l0 + 1L;
        i1 = i1 + 1;
        goto label0;
    
     label1:
        java.lang.System.out.println(l0);
        l2 = 0L;
        i3 = 1073741823;
    
     label2:
        if i3 >= 2147483647 goto label3;
    
        l2 = l2 + 1L;
        i3 = i3 + 1;
        goto label2;
    
     label3:
        java.lang.System.out.println(l2);
        return;
    

    Eclipse 编译器输出

    public static void main(java.lang.String[]);
      Code:
       0:   ldc #16; //int 1073741823
       2:   istore_1
       3:   ldc #17; //int 2147483647
       5:   istore_2
       6:   lconst_0
       7:   lstore_3
       8:   ldc #16; //int 1073741823
       10:  istore  5
       12:  goto    22
       15:  lload_3
       16:  lconst_1
       17:  ladd
       18:  lstore_3
       19:  iinc    5, 1
       22:  iload   5
       24:  ldc #17; //int 2147483647
       26:  if_icmplt   15
       29:  getstatic   #18; //Field java/lang/System.out:Ljava/io/PrintStream;
       32:  lload_3
       33:  invokevirtual   #24; //Method java/io/PrintStream.println:(J)V
       36:  lconst_0
       37:  lstore_3
       38:  ldc #16; //int 1073741823
       40:  istore  5
       42:  goto    52
       45:  lload_3
       46:  lconst_1
       47:  ladd
       48:  lstore_3
       49:  iinc    5, 1
       52:  iload   5
       54:  ldc #17; //int 2147483647
       56:  if_icmplt   45
       59:  getstatic   #18; //Field java/lang/System.out:Ljava/io/PrintStream;
       62:  lload_3
       63:  invokevirtual   #24; //Method java/io/PrintStream.println:(J)V
       66:  return
    

    Grimp 输出:

        java.lang.String[] r0;
        int i0, i1, i3, i5;
        long l2, l4;
    
        r0 := @parameter0;
        i0 = 1073741823;
        i1 = 2147483647;
        l2 = 0L;
        i3 = 1073741823;
        goto label1;
    
     label0:
        l2 = l2 + 1L;
        i3 = i3 + 1;
    
     label1:
        if i3 < 2147483647 goto label0;
    
        java.lang.System.out.println(l2);
        l4 = 0L;
        i5 = 1073741823;
        goto label3;
    
     label2:
        l4 = l4 + 1L;
        i5 = i5 + 1;
    
     label3:
        if i5 < 2147483647 goto label2;
    
        java.lang.System.out.println(l4);
        return;
    

    有趣的是,javac 生成的版本在int 上使用if_icmpge 作为退出条件(>= 2147483647),这应该没有意义(等于,但不是大于)。不过,两者看起来都是正确的,所以我怀疑是 JVM 错误。

    【讨论】:

      【解决方案3】:

      这可能是在 64 位服务器 VM 中的 HotSpot 中展开的错误循环。尝试使用-client+XX:-AggressiveOpts 运行代码。

      【讨论】:

        【解决方案4】:

        当上限接近Integer.MAX_VALUE 时,会出现影响 for 循环的错误。

        this question

        【讨论】:

        • Eclipse 生成的字节码似乎与end = Integer.MAX_VALUE-1 配合得很好,所以它似乎确实是Integer.MAX_VALUE 特有的。