Confusing output from infinite recursion within try-catch

前端 未结 7 1979
隐瞒了意图╮
隐瞒了意图╮ 2020-12-23 19:13

Consider the following code.

public class Action {
private static int i=1;
public static void main(String[] args)          


        
相关标签:
7条回答
  • 2020-12-23 19:44

    As the other answers already explain it has to do with System.out.println requiring extra space on the stack and thus throwing itself a StackOverflowError.

    Try this code here to see some different behaviour which shows that at some point there is exceptions being thrown all over the place so that i++ cannot occur anymore.

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

    The important lession to learn is: Never catch Errors as they really indicate that there is something serious wrong with your JVM which cannot be handled normally.

    0 讨论(0)
  • 2020-12-23 19:51

    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.

    0 讨论(0)
  • 2020-12-23 19:55

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

    0 讨论(0)
  • 2020-12-23 20:02

    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.

    0 讨论(0)
  • 2020-12-23 20:10

    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.
    0 讨论(0)
  • 2020-12-23 20:10

    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.

    0 讨论(0)
提交回复
热议问题