13

Why does this code totally destroy the output?

public class Main {
    public static void main(String[] args) {
        System.out.println();

        rec();
    }

    private static int rec() {
        try {
            return rec();
        } catch(StackOverflowError e) {
            System.out.println("Boo.");
            return 0;
        }
    }
}

Sample output I get: Boo.Boo.Boo.Boo.Boo.Boo.Boo.Boo.Boo.

dragostis
  • 2,574
  • 2
  • 20
  • 39
  • 1
    I get only one Boo, when I run the code. – libik Mar 24 '14 at 17:33
  • 1
    Maybe try to explain how you think this is _supposed_ to work. – takendarkk Mar 24 '14 at 17:33
  • 1
    Are you asking why you get multiple boo's outputted, instead of one? Are your boo's on one line? – NESPowerGlove Mar 24 '14 at 17:33
  • 2
    It should only output `Boo.` – dragostis Mar 24 '14 at 17:34
  • 8
    My guess is that the call to System.out.println may be increasing the stack enough to cause another stackoverflow within the catch, which propagates down to the next rec() call, which is handled again, prints boo, but causes another stackoverflow with another print and so on until you get enough headroom eight or so rec() calls down to catch the exception and print with no problems. – NESPowerGlove Mar 24 '14 at 17:37
  • What version of Java are you running? I only get one `Boo.` on Java 1.7.0_51 (Windows 7 x64). – Henry Keiter Mar 24 '14 at 17:38
  • It might the case, but not calling `println()` in the `main` method does prevent the problem. – dragostis Mar 24 '14 at 17:39
  • java 1.7.0_51 OpenJDK Runtime Environment (7u51-2.4.4-0ubuntu0.12.04.2) – dragostis Mar 24 '14 at 17:40
  • I get the same result on ideone.com too. – dragostis Mar 24 '14 at 17:41
  • 4
    @NESPowerGlove: This makes sense as “Boo.” is printed but the newline is not. So it fails on printing the newline which implies a `flush` which might require more stack space than a simple `print` without `ln`. – Holger Mar 24 '14 at 17:46
  • 1
    The `PrintStream` class buffers the characters first, then it tries to write them to the stream which involves `Charset` conversion etc. which is much more complex. When this fails with another `StackOverflowError`, the text is already in the buffer. This repeats until one write of the *entire buffer* does not fail with an SOE. – Holger Mar 24 '14 at 18:06

1 Answers1

9

The problem is similar to the problem in this Question: Stack overflow error handling in finally block

Basically, the println call your handler for StackOverflowException is triggering a further StackOverflowException. This is being handled in the enclosing call to rec(). This continues (unwinding the rec() calls as we go) until the exception handler has enough space to complete the println call. The intermediate calls are probably adding characters to the output buffer, and then raising the SOE.

A precise explanation would require forensic analysis of the code of PrintStream and the stream stack ... all the way to the point where it calls into native code.


If you want a meta-explanation, it is that your code is attempting to recover from an Error ... which the javadocs say is something that you should not attempt to do. As a general rule, after an Error, the JVM is going to be an uncertain, and possibly broken state. In this case, the uncertainty manifests as data that may or may not have been written into the buffer. The behaviour is (probably) deterministic, but definitely hard to analyse and impossible to predict without a proper analysis.

Community
  • 1
  • 1
Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • Yes. NESPowerGlove's comment was pretty much as explicit if not more so. How come that commenting out the `println` from the `main` method does make a difference? – dragostis Mar 24 '14 at 18:07
  • 1
    I remember there is another question on SO with in depth analysis of the reason, but I can't find that :( – Rohit Jain Mar 24 '14 at 18:10