Java中的错误for循环?

23

我运行以下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

你能重现它吗?这是一个错误吗?

编辑:奇怪

final int end = Integer.MAX_VALUE - 1;

运行正常。


2
必须还有其他错误,因为我在两个循环中都得到了1073741824的值。 - leifg
3
我已经在运行OS X 10.6.8的MacBook上重现了这个漏洞。 - Björn Pollex
2
运行 #1: 1073741811 76800运行 #2: 1073741811 77824运行 #3: 1073741811 60416运行 #4: 1073741811 53248在 Windows 7 64 位上使用 jdk1.6.0_25。 - hakon
2
1.6.0.24在Windows 7上,只是想指出即使第一行似乎不正确。 - leifg
2
如果我直接运行它会出现错误,但是如果我进行调试,那么它就能正常工作。如果有错误,那么就是 JITC 错误。 - ssedano
显示剩余12条评论
4个回答

13

我能够使用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

输出的结果为:

    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的错误。


6

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

请参见此问题


1
Eclipse生成的字节码似乎对end = Integer.MAX_VALUE-1可以正常工作,因此这确实是与Integer.MAX_VALUE有关的。 - Bruno

2

根据Java专家的说法,这是Bug 5091921,已在JDK7中修复,但不计划在JDK6中进行修复。


0

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接