Confusing output from infinite recursion within tr

2020-02-17 03:58发布

Consider the following code.

public class Action {
private static int i=1;
public static void main(String[] args) {
    try{
        System.out.println(i);
        i++;
        main(args);
    }catch (StackOverflowError e){
        System.out.println(i);
        i++;
        main(args);
    }
 }

}

I am getting i value up to 4338 correctly. After catching the StackOverflowError output getting wired as follows.

4336
4337
4338 // up to this point out put can understand 
433943394339 // 4339 repeating thrice  
434043404340
4341
434243424342
434343434343
4344
4345
434643464346
434743474347
4348
434943494349
435043504350

Consider Live demo here. It is working correctly up to i=4330. Actually how this happen?

FYI:

I did following code to realize what is happening here.

public class Action {

    private static int i = 1;
    private static BufferedWriter bw;

    static {
        try {
            bw = new BufferedWriter(new FileWriter("D:\\sample.txt"));
        } catch (IOException e) {
           e.printStackTrace();
        }
    }

    public static void main(String[] args) throws IOException {
        bw.append(String.valueOf(i)+" ");
        try {
            i++;
            main(args);
        } catch (StackOverflowError e) {
            bw.append(String.valueOf(i)+" ");
            i++;
            main(args);
        }
    }

}

Now previous issue not there. now value of i up to 16824744 correct and and runs further. I am hopping this may runs up to value of i=2,147,483,647(max value of int) without an issue.

There is some issue with println(). There are similar answers bellow too. But why?

What will be the actual reason?

7条回答
祖国的老花朵
2楼-- · 2020-02-17 04:34

The bottom line is that StackOverflowError is an error, not an exception. You are handling an error, not an exception.So the program has already crashed when it enters the catch block.Regarding the strange behaviour of the program, below is the explanation on basis of java buffers from this official doc:

For example, an autoflush PrintWriter object flushes the buffer on every invocation of println or format

The System.out.println() internally calls the PrintStream which is buffered. You don't loose any data from the buffer, it gets all written to the output( terminal in your case) after it fills up, or when you explicitly call flush on it.

Coming back to this scenario, it depends on the internal dynamics of how much the stack is filled up and how many print statements were able to get executed from the catch in main() and those number of characters were written to the buffer. Here after the first try is executed, i.e. in the event of first occurence of the stack overflow, the first System.out.println() fails to print the new line so it flushed the buffer with remaining characters.

查看更多
太酷不给撩
3楼-- · 2020-02-17 04:41

Note the absence of newline characters in 433943394339. It indicates that something wrong happens inside System.out.println().

The essential point here is that System.out.println() requires some stack space to work, so that StackOverflowError is thrown from System.out.println().

Here is your code with marked points:

public static void main(String[] args) {
    try{
        System.out.println(i); // (1)
        i++;
        main(args); // (2)
    }catch (StackOverflowError e){
        System.out.println(i); // (3)
        i++;
        main(args); // (4)
    }
}

Let's imagine what happens at level N of recursion when i = 4338:

  • Statement (1) at level N prints 4338. Output 4338\n
  • i is incremented to 4339
  • Control flow enters level N + 1 at (2)
  • Statement (1) at level N + 1 tries to print 4339, but System.out.println() throws a StackOverflowError before it prints a newline. Output 4339
  • StackOverflowError is caught at level N + 1, statement (3) tries to print 4339 and fails for the same reason again. Output 4339
  • Exception is caught at level N. At this point there is more stack space available, therefore statement (3) tries to print 4339 and succeeds (newline is printed correctly). Output 4339\n
  • i is incremented and control flow enters level N + 1 again at (4)

After this point the situation repeats with 4340.

I'm not sure why some numbers are printed correclty between sequences without newlines, perhaps its related to internal work of System.out.println() and buffers it uses.

查看更多
疯言疯语
4楼-- · 2020-02-17 04:45

If the execution of println (or one of the methods called by it) causes a stack overflow, you will print the same i value from the catch clause of the enclosing main incarnation.

The exact behavior is rather unpredictable as it depends on the stack space still available.

查看更多
放荡不羁爱自由
5楼-- · 2020-02-17 04:51

According to my test:

When Exception is thrown by try Block, i has same value when it comes in catch block (as its not incremented due to exception)

and then inside catch block same exception is thrown and which is again caught by catch block !

I tried Following Code

try {
            System.out.println("Try " + i);
            i++;
            main(args);
        } catch (StackOverflowError e) {
            System.out.println("\nBefore");
            System.out.println("Catch " + i);
            i++;
            System.out.println("After");
            main(args);

        }

Output :

Try 28343
Try 28344
Before
Before
Before
Before
Catch 28344
After
Try 28345
Try 28346
Try 28347
Try 28348
Before
Before
Before
Before
Catch 28348
After
Try 28349

when try block throws exception it is caught by catch block but when it goes to System.out.println("Catch " + i); again Exception is thrown 4 times (in my eclipse) Without printing System.out.println("Catch " + i);

As in above output, i have tested it by printing "Before" Text which is printed four times before it prints System.out.println("Catch " + i);

查看更多
老娘就宠你
6楼-- · 2020-02-17 04:53

axtavt Answer is very complete but I'd like to add this:

As you may know the stack is used to store the memory of variables, based on that you cannot create new variables when you reach the limit, it is true that System.out.println will need some stack resources

787     public void More ...println(Object x) {
788         String s = String.valueOf(x);
789         synchronized (this) {
790             print(s);
791             newLine();
792         }
793     }

Then after calling the print, the error does not allow you to even call the newLine, it breaks again right on the print. Based on that you can make sure that's the case by changing your code like this:

public class Action {
    static int i = 1;

    public static void main(String[] args) {
        try {
            System.out.print(i + "\n");
            i++;
            main(args);
        } catch (StackOverflowError e) {
            System.out.print(i + " SO " + "\n");
            i++;
            main(args);
        }
    }
}

Now you will not ask the stack to handle the new lines, you will use the constant "\n" and you may add some debugging to the exception printing line and your output will not have multiple values in the same line:

10553
10553 SO
10553 SO
10554
10554 SO
10554 SO
10555
10556
10557
10558

And it will keep broken until get some resources to allocate new data and pass to the next i value.

查看更多
看我几分像从前
7楼-- · 2020-02-17 04:56

What I suspect being happening is this:

  1. Print i
  2. Print newline
  3. Increase i
  4. Enter main
  5. Print i
  6. Print newline
  7. Increase i
  8. Enter main
  9. Print i
  10. StackOverflow got thrown (instead of print newline)
  11. Return to main, now in the catch
  12. Print i
  13. StackOverflow got thrown again (instead of print newline)
  14. Return to main, in another catch body.
  15. Print i
  16. Print newline (now doesn't fail anymore, because we are two levels higher)
  17. Enter main, and go back to 1.
查看更多
登录 后发表回答