There is this code:
public class Main {
public static void main(final String[] args) throws Exception {
System.out.print("1");
doAnything();
System.out.println("2");
}
private static void doAnything() {
try {
doAnything();
} catch (final Error e) {
System.out.print("y");
}
}
}
And there is the output:
1yyyyyyyy2
Why does it print "y" eight times and no more. How can Java call println()
when StackOverflowError
encountered?
In addition, Objects of type
Error
are notExceptions
objects they represent exceptional conditions.Errors
represent unusual situations that are not caused by program errors, usually does not normally happen during program execution, such as JVM running out of memory. Although they share a common superclassThrowable
, meaning both can be thrown, it can be placed in acatch
but generally not supposed to be caught, as they represent rare, difficult-to-handle exceptional conditions.One thing is clear that System.out.print("y"); in catch creates this puzzle. If we change the code as
it prints
Stack overflow.
You are only printing on exception,in the meantime the program recurses into overflow.
At which point this occurs depends on individual systems, memory, etc.
What is the purpose of the program?
Well the no. of times the stack overflow error is hit is undefined. However, the JVM allows you to recover from
StackOverflowError
error and continue execution of the system normally.It is proved by the following code:
Note however, as @Javier said, the
StackOverflowError
is thrown by the JVM synchronously or asynchronously(which means it can be thrown by another thread, possibly a native thread) which is why it is not possible to get the stack trace of the error. The no. of times the thread(s) hit thecatch()
block is undefined.Here you are catching
Error
and notException
in which case your program would have crashed.If you try this code (modified to add a static counter)
Output
So, it has got
stackerror
6869 times(changes for different runs) and the last value is printed. If you just print they
as you did earlier then it might the case that the output is getting bufferred and not getting flushed as it is not aprintln
.Update
The
System.out.println
internally calls thePrintStream
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
doAnything()
and those number of characters were written to the buffer. In the main back it finnally get's printed with the number2
.javadoc reference to buffered streams
The first time the
StackOverFlowError
occurs, the call to the lastdoAnything()
is cancelled and the control is returned to the catch block from the lastdoAnything()
.However, because the stack is still practically full, the simple fact of calling
System.out.print("y")
will causes anotherStackOverflowError
because of the need of pushing some value on the stack and then make a call to the functionprint()
.Therefore, another
StackOverflowError
occurs again and the return is now returned on the catch{} block of the previousdoAnything()
; where anotherStackOverflowError
will happens because the need of stack space required to do a single call toSystem.out.println("y")
is greater than the amount of space liberated from returning a call fromdoAnything()
.Only when there will be enough space on the stack to execute a call to
System.out.print("y")
that this process will stop and a catch block will successfully complete. We can see that by running the following piece of code:Notice that a
println(a)
is used instead of aprint(a)
; therefore a new line should be printed after each value ofa
if everything runs OK.However, when I run it, I get the following result:
This means that there have been 17 attempts ro run the catch block. Of these catch block executions, 9 are unable to print anything before generating themselves a StackOverflowError; 7 are able to print the value of 6190 but are unable to print a newline after it before themselves rising an error again and finally, there is one that is able to both print the value of 6190 and the newline after it; therefore finally permitting its catch block to complete without any new StackOverflowError and return gracefully up the calls stack.
As we are dealing with StackOverflowError, these numbers are only an example and will vary greatly not only between machines but also between executions and the simple fact of adding or removing any kind of instructions should also change these values. However, the pattern seen here should remains the same.