Java 中的错误 for 循环?

发布于 2024-11-28 18:51:20 字数 883 浏览 2 评论 0原文

我观察到运行以下 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;

作品很好。

I observed erroneous behaviour running the following java-code:

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);
    }
  }
}

Both loops do exactly the same. Nevertheless, the second one outputs a non-deterministic erroneous value. I'm running the code on Linux using Version:

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)

Sample Output:

1073741811
141312

Can you reproduce it? Is it a bug?

Edit: Strange

final int end = Integer.MAX_VALUE - 1;

works fine.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

很酷不放纵 2024-12-05 18:51:20

我可以使用 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

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

I'm able to reproduce it with the .class file produced by Eclipse, but not when compiling on the command line with javac.

The bytecode generated differs:

javac output

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

For something easier to read, here is the Grimp output produced by Soot:

    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 compiler output

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 output:

    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;

Interestingly, the javac-produced version uses if_icmpge as an exit condition (>= 2147483647) on an int, which shouldn't make sense (the equal does, but not the greater than). Both look correct, though, so I'd suspect a JVM bug.

抚你发端 2024-12-05 18:51:20

有些错误会影响 for 循环,特别是当上限接近 Integer.MAX_VALUE 时。

请参阅此问题

There are bugs that affect for loops specifically when the upper bound is close to Integer.MAX_VALUE.

See this question.

雨后彩虹 2024-12-05 18:51:20

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

According to the Java gurus this is bug 5091921, fixed in JDK7 but not planned for a fix in JDK6.

柏林苍穹下 2024-12-05 18:51:20

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

It is possible it is the erroneous loop unrolling in HotSpot in 64-bit server VM. Try running the code with either -client or +XX:-AggressiveOpts.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文