23

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.

Björn Pollex
  • 75,346
  • 28
  • 201
  • 283
Sebastian
  • 281
  • 1
  • 8
  • 2
    There must be another error, as I get in both loops the value 1073741824 – leifg Aug 09 '11 at 11:13
  • 3
    I have reproduced this bug on a MacBook running OS X 10.6.8. – Björn Pollex Aug 09 '11 at 11:14
  • @leifg: What system/JDK are you running on? – Björn Pollex Aug 09 '11 at 11:15
  • Can't reproduce on Linux with Sun Java 6u26 or OpenJDK 6b22. – Joachim Sauer Aug 09 '11 at 11:17
  • 2
    run #1: 1073741811 76800 run #2: 1073741811 77824 run #3: 1073741811 60416 run #4: 1073741811 53248 Using jdk1.6.0_25 on Windows 7 64 bit – hakon Aug 09 '11 at 11:18
  • 1
    Unable to reproduce. Have you tried debugging it? – Qwerky Aug 09 '11 at 11:18
  • 2
    1.6.0.24 on Windows 7, just wanted to point out taht even the first line seems not to be correct – leifg Aug 09 '11 at 11:18
  • 2
    Reproduced on MAC. Values are 1073741811 and 97280 – Sathwick Aug 09 '11 at 11:20
  • You should try to determine whether it's a javac bug or a JITC bug. Most likely a JITC bug, and it will go away if the range of the loops is reduced to a small number (eg, below 1000). – Hot Licks Aug 09 '11 at 11:24
  • both reproduced same output 1073741824 1073741824 Version, On Mac java version "1.6.0_26" Java(TM) SE Runtime Environment (build 1.6.0_26-b03-383-11A511c) Java HotSpot(TM) 64-Bit Server VM (build 20.1-b02-383, mixed mode) – TeaCupApp Aug 09 '11 at 11:24
  • Not sure it's exactly the right place, but I reported it to hotspot-dev-request@openjdk.java.net. – Hot Licks Aug 09 '11 at 11:25
  • When reporting results, do a `java -version` and report the version displayed. – Hot Licks Aug 09 '11 at 11:25
  • 2
    If I run it bug, but if I debug it then works fine. If a bug, then JITC bug. – ssedano Aug 09 '11 at 11:33
  • can you post the .class file as well? – amit Aug 09 '11 at 11:36
  • On Windows 7 64-bit, running 32-bit 1.6.0_26 Java both values end up the same: 1073741824 1073741824 – Maggie Aug 09 '11 at 11:42
  • I'm getting the same result as leifg and Maggie. Ran on RHEL 5.4 x64, JDK6u25 x64. $ java -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) – RednaxelaFX Aug 09 '11 at 11:48
  • Same as @Maggie on Windows 7 x64 running "1.6.0_35", Java(TM) SE Runtime Environment (build 1.6.0_35-b10), Java HotSpot(TM) 64-Bit Server VM (build 20.10-b01, mixed mode). – Miserable Variable Oct 22 '12 at 17:15

4 Answers4

13

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.

Bruno
  • 119,590
  • 31
  • 270
  • 376
6

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

See this question.

Community
  • 1
  • 1
starblue
  • 55,348
  • 14
  • 97
  • 151
  • 1
    The Eclipse-generated bytecode seems to work just fine with `end = Integer.MAX_VALUE-1`, so it seems specific to `Integer.MAX_VALUE` indeed. – Bruno Aug 09 '11 at 12:29
2

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

Michael McGowan
  • 6,528
  • 8
  • 42
  • 70
Hot Licks
  • 47,103
  • 17
  • 93
  • 151
0

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.

Denis Tulskiy
  • 19,012
  • 6
  • 50
  • 68