JDK-6935022 : Server VM incorrectly breaks out of while loop
  • Type: Bug
  • Component: hotspot
  • Sub-Component: compiler
  • Affected Version: 6u18
  • Priority: P3
  • Status: Closed
  • Resolution: Duplicate
  • OS: solaris_10
  • CPU: sparc
  • Submitted: 2010-03-15
  • Updated: 2011-02-16
  • Resolved: 2010-04-15
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 6 JDK 7
6-poolResolved 7-poolResolved
Related Reports
Duplicate :  
Relates :  
Description
The below test case shows the problem.  The problem occurs after anywhere between 100-1000 iterations of main's while loop, depending on the Java version and/or the architecture.  The problem is seen on all SPARC server VM's 1.4.2 through 7.0. I didn't test non-SPARC platforms.  The problem is NOT seen on client VM.

The problem is that after several hundred iterations of main's while loop, the private loop(int endingrow) method runs through as if there is no while loop in it (all code is run exactly once) and continues to do so for every iteration of main's while loop thereafter.


Expanded code explanation if needed:

For every iteration of main's while(true) loop, the private method loop(int) is run,
which initializes local variables:

int rows = 1; 
boolean next = true;
(int endingrow is passed by value from main (Integer.MAX_VALUE))

and runs through 11 iterations of its while loop 

while(rows <= endingRow && next)

where it simply increments rows by 1, prints the values of the local variables, and changes the value of boolean next to false when rows is 12.

rows should never be greater than endingrow because boolean next is changed to false when rows == 12 (vis a vis the boolean next(int) method), causing the  (rows <= endingRow && next), so the only condition under which we should come out of the loop would be when "next" is false, in which case the exception after the loop:

if (next)
            throw new Exception("Ended on rows(no rs): " + rows);

should never be thrown.

When next becomes false, we fail the while loop test, fail the test to throw the exception and return to main where we loop through main's while(true) loop again, which calls the loop(int) method again, which initializes the local varaibles and loops through 11 iterations of its while(rows <= endingRow && next) loop again.

since main's while loop is always true, this cycle should run continuously.

But with the server VM, after several hundred iterations of main's while loop, the first iteration of the loop(int) method's while loop runs through, but does not actually loop back for the second iteration. Even though we see rows is 2 and next is true (values which should pass the while loop's test), it breaks out of the loop and throws the exception:

Caught on iteration 883
java.lang.Exception: Ended on rows(no rs): 2
        at NewSimple.loop(NewSimple.java:50)
        at NewSimple.main(NewSimple.java:16)

And thereafter, for every iteration of main's while loop, the code in the private loop() function is run as if there is no while loop: in other words, all the code is run exactly once, including the exception being thrown.

We have seen this problem go away with the following code changes:

1. the System.out.println("Rows="+rows+", end="+endingRow+", next="+next); is removed,
2. the  while(rows <= endingRow && next) is changed to:
       while(rows - endingRow <= 0 && next)
3.endingRow is declared inside loop, instead of passing as an argument from main. 


Test Case:

public class NewSimple 
{
    public static final void main(String[] args)
        throws Exception
    {
        NewSimple test = new NewSimple();
        
        int cnt = 0;

        while (true)
        {
            try
            {
                ++cnt;
                System.out.println(cnt);
                test.loop(Integer.MAX_VALUE);
            }
           
            catch (Exception e)
            {
                System.out.println("Caught on iteration " + cnt);
                e.printStackTrace();
                
                try
public class NewSimple
{
    public static final void main(String[] args)
        throws Exception
    {
        NewSimple test = new NewSimple();

        int cnt = 0;

        while (true)
        {
            try
            {
                ++cnt;
                System.out.println("Thread="+Thread.currentThread().getName());
                test.loop(2147483647);
            }

            catch (Exception e)
            {
                System.out.println("Caught on iteration " + cnt);
                e.printStackTrace();

                try
                {
                    Thread.sleep(10000);
                }
                catch(Exception ie)
                {}
            }
        }
    }

    private void loop(int endingRow)
        throws Exception
    {
        int rows = 1;
        boolean next = true;

        while(rows <= endingRow && next)
        {
            rows++;
            System.out.println("Rows="+rows+", end="+endingRow+", next="+next);
           

            next = next(rows);
        }

        if (next)
            throw new Exception("Ended on rows(no rs): " + rows);
    }

    private boolean next(int rows)
    {
        return rows < 12;
    }
}

Comments
EVALUATION It's a well known issue with MAX_INT as a loop limit. Problem is with canonicalization of loop exit test in preparation for loop transformation. while (i+N <= limit) becomes while (i < limit+N+1)
18-03-2010