Confusion of output from infinite recursion in try-catch

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 correctly evaluating the value of 4338 . After you catch the output of StackOverflowError , which will be connected 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 here. It works correctly up to i=4330 . Actually, how does this happen?

FYI:

I made the following code to understand what is going on 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 the previous problem does not exist. now the value of i until 16824744 fixed and continues. I am sure that this can lead to a value of i=2,147,483,647 (maximum value of int) without problems.

There is a problem with println() . Similar answers are also given below. But why?

What will be the actual reason?

+53
java stack-overflow
Aug 19 '13 at 10:15
source share
7 answers

Note the absence of newlines in 433943394339 . This indicates that something wrong is happening inside System.out.println() .

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

Here is your code with marked dots:

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

Imagine what happens at level N recursion when i = 4338 :

  • Statement (1) at the N level outputs 4338 . Pin 4338\n
  • i increases to 4339
  • The control flow arrives at level N + 1 in (2)
  • Statement (1) at the N + 1 level tries to print 4339 , but System.out.println() throws a StackOverflowError before printing a new line. Exit 4339
  • StackOverflowError falls to level N + 1, statement (3) tries to print 4339 and again crashes for the same reason. Exit 4339
  • The exception is caught at level N. At this point, more stack space is available, so operator (3) tries to print 4339 and succeeds (new line printed correctly). Conclusion 4339\n
  • i increases, and the control flow again goes to the level N + 1 in (4)

After this point, the situation is repeated using 4340 .

I'm not sure why some numbers are printed according to sequences without newlines, perhaps this is due to the internal operation of System.out.println() and the buffers used.

+51
Aug 19 '13 at 10:50 on
source share

I suspect this is happening:

  • Print i
  • Print a new line
  • Enlarge i
  • Enter the main thing
  • Print i
  • Print a new line
  • Enlarge i
  • Enter the main thing
  • Print i
  • StackOverflow got a throw (instead of printing a new line)
  • Return to the main thing, now in catch
  • Print i
  • StackOverflow was reset again (instead of printing a new line)
  • Go back to the main one, to another catch body.
  • Print i
  • Printing a new line (now no longer works, because we are two levels higher)
  • Type main and go back to 1.
+17
Aug 19 '13 at 10:51 on
source share

According to my test:

When an exception is thrown by try block, i has the same meaning when it enters the catch block (since it is not raised due to the exception)

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

I tried the 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); } 

Exit:

 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 eliminates the lock, it is caught by the catch block, but when it returns to System.out.println("Catch " + i); again the Exception is thrown 4 times (in my eclipse) Without printing System.out.println("Catch " + i);

As in the previous issue, I checked it by typing the text "Before", which is printed four times before it prints System.out.println("Catch " + i);

+4
Aug 19 '13 at 11:01
source share

If executing println (or one of the methods it invokes) results in a stack overflow, you will print the same i value from the catch clause of the nested main incarnation.

The exact behavior is quite unpredictable, since it depends on the free space of the stack.

+3
Aug 19 '13 at 10:50 on
source share

As the other answers already explain, this is due to System.out.println requiring extra stack space and thus throwing itself into a StackOverflowError.

Try this code here to see another behavior that shows that at some point there are exceptions that are thrown everywhere, so that I ++ can no longer happen.

 public class Action { private static int i = 1; private static StringBuffer buffer = new StringBuffer(); public static void main(String[] args) { try { print(); main(args); } catch (StackOverflowError e) { print(); main(args); } } private static void print() { buffer.append(i).append("\n"); i++; if (i % 1000 == 0) { System.out.println("more: " + buffer); buffer = new StringBuffer(); } } } 

An important lesson to learn is: Never break Errors, as they really indicate that something serious does not work out with your JVM, that you cannot handle it normally.

+1
Aug 19 '13 at 10:54 on
source share

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 behavior of the program, the following is an explanation based on java buffers from this white paper:

For example, a PrintWriter object with autoload clears the buffer every println or format call

System.out.println() internally calls PrintStream , which is buffered. You do not lose data from the buffer, everything is written to the output (the terminal in your case) after it is filled, or when you explicitly call a flash on it.

Returning to this scenario, it depends on the internal dynamics of how full the stack is and how many print reports it was possible to execute from catch in main() , and these numbers of characters were written to the buffer. Here, after the first attempt, that is, in the case of the first appearance, the first System.out.println () cannot print a new line so that it flushes the buffer with the remaining characters.

+1
Aug 19 '13 at 11:08 on
source share

axtavt Answer is very complete, but I would like to add this:

As you know, the stack is used to store the memory of variables, based on which you cannot create new variables when you reach the limit, it is true that System.out.println will require several 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 even allow you to call a new line, it again breaks directly to the print. Based on this, you can verify this by changing your code as follows:

 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 process new lines, you will use the constant "\ n", and you can add some debugging to the exception print line, and your output will not have multiple values ​​on one line:

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

And it will continue to be interrupted until several resources are allocated to distribute new data and move on to the next value of i.

0
Sep 09 '13 at 19:10
source share



All Articles