Why is it possible to recover from a StackOverflowError?
I'm surprised at how it is possible to continue execution even after a StackOverflowError
has occurred in Java.
I know that StackOverflowError
is a sublass of the class Error.
The class Error is decumented as "a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch."
This sounds more like a recommendation than a rule, subtending that catching a Error like a StackOverflowError is in fact permitted and it's up to the programmer's reasonability not to do so. And see, I tested this code and it terminates normally.
public class Test
{
public static void main(String[] args)
{
try {
foo();
} catch (StackOverflowError e) {
bar();
}
System.out.println("normal termination");
}
private static void foo() {
System.out.println("foo");
foo();
}
private static void bar() {
System.out.println("bar");
}
}
How can this be? I think by the time the StackOverflowError is thrown, the stack should be so full that there is no room for calling another function. Is the error handling block running in a different stack, or what is going on here?
Solution 1:
When the stack overflows and StackOverflowError
is thrown, the usual exception handling unwinds the stack. Unwinding the stack means:
- abort the execution of the currently active function
- delete its stack frame, proceed with the calling function
- abort the execution of the caller
- delete its stack frame, proceed with the calling function
- and so on...
... until the exception is caught. This is normal (in fact, necessary) and independent of which exception is thrown and why. Since you catch the exception outside of the first call to foo()
, the thousands of foo
stack frames that filled the stack have all been unwound and most of the stack is free to be used again.
Solution 2:
When the StackOverflowError is thrown, the stack is full. However, when it's caught, all those foo
calls have been popped from the stack. bar
can run normally because the stack is no longer overflowing with foo
s. (Note that I don't think the JLS guarantees you can recover from a stack overflow like this.)
Solution 3:
When the StackOverFlow occurs, the JVM will pop down to the catch, freeing the stack.
In you example, it get rids of all the stacked foo.