Stack overflow error handling in finally block
Asked Answered
T

5

5

I have a program in java, which runs infinite times.

Program code:

void asd()
{
    try
    {
        //inside try block
        System.out.println("Inside try !!!");
        asd();
    }
    finally
    {
        //inside finally
        System.out.println("Inside finally !!!");
        asd();
    }
}

OUTPUT : this program runs infinitely, by constantly printing both the sysouts.

My question : At some point, it starts throwing StackOverflowErrors from the try block and so it reaches the finally block, where we are again calling this function recursively. But how is the recursive function in the finally block being executed as we already facing a StackOverflowError?

How does the JVM handle this situation? Will the same behavior occur if we get OutOfMemoryErrors too?

Thoroughfare answered 24/7, 2013 at 13:55 Comment(0)
N
4

The problem is that your example program is pathological. It won't work, and it can't work.

But how the recursive function in the finally block gets executed as we already facing StackOverflowError.?

There is a rather complicated sequence of calls going on. Lets assume that the stack can hold 3 frames. A "hand execution" gives us a sequence of calls / call-stack snapshots as follows:

asd()
asd() > try
asd() > try > asd() 
asd() > try > asd() > try 
asd() > try > asd() > try > asd() // Stack Overflow!
asd() > try > asd() > finally
asd() > try > asd() > finally > asd() // Stack Overflow!
asd() > finally
asd() > finally > asd()
asd() > finally > asd() > try
asd() > finally > asd() > try > asd() // Stack Overflow!
asd() > finally > asd() > finally
asd() > finally > asd() > finally > asd() // Stack Overflow!

END

As you can see with a stack of depth 3, we made 7 calls, 4 of which failed with a stack overflow. If you do the hand execution for a stack of depth 4, you will get 15 calls, 5 => 31. The pattern is N => 2**N - 1 calls.

In your case, the default stack is going to be able to accommodate hundreds, or even thousands of recursive calls.

Say N = 100. 2**100 is a very large number of calls. It is not infinite, but you will probably be dead before the program terminates.

How does the JVM handle this situation?

As above. The JVM is not doing anything special. The "effectively infinite loop" behaviour is entirely down to the way that your program is written.

Will the same behaviour occur if we get OutOfMemoryErrors too?

Erm ... it depends on your program. But I'm sure you could concoct an example program that exhibited a similar pattern of behaviour.

Nerland answered 24/7, 2013 at 14:32 Comment(0)
R
2

Errors, i.e. OutOfMemoryError, StackOverflowError, etc. are not intended to be handled. They leave JVM in undefined state, nothing is guaranteed. At this point your application must simply terminate and you must fix the problem which lead to this.

Your application should not try to handle errors OR run after one happened. If you're calling the recursive function at this point then you are the one to blame. The result is not predictable.

See "11.1.1. The Kinds of Exceptions": http://docs.oracle.com/javase/specs/jls/se7/html/jls-11.html

Ruralize answered 24/7, 2013 at 14:3 Comment(0)
A
2

Suppose the program is executing the asd() method and the stack space is just about to end. Also suppose that instead of "inside try" and "inside finally" the method prints a counter that tells how far the stack you are:

void asd(int i){
    try{
        //inside try block
        System.out.print(i);
        System.out.println("t");
        asd(i+1);
    }
    finally{
        //inside finally
        System.out.print(i);
        System.out.println("f");
        asd(i+1);
    }
}

}

Now this is what the program does when it's just about to run out of stack space, when i is 9154.

The call to println("t") outputs the characters, and then goes on to call the println() method. This makes the program run out of stack space, so the execution is moved to the finally block. The call to println there again runs out of stack space when printing the new line. The error is thrown again, and the execution moves on to the finally in the activation frame above the current method call. This makes the program print f for the second time, and since we've popped an activation frame from the stack this call now completes normally, and prints out a new line. The program has so far given this output:

...
9152t
9153t
9154t9154f9153f              // notice we jumped to i=1953

Now, the method calls itself again, now from the finally block. The situation with the stack space is just like before, so the program executes like above, except that, since we were in a finally block for the method call of i=1953, the program execution ends up in the finally block of the method call of i=1952:

9154t9154f9152f

The finally block of i=9152 calls asd again, passing i=9153, and since now there's enough stack space to print a complete line the method outputs from the try-block:

9153t

and then goes on to call itself, and in this call will end up running out of stack space again, giving the output:

9154t9154f9153f

... and the rest of the output can be explained in a similar manner:

9154t9154f9151f
9152t
9153t
9154t9154f9153f
9154t9154f9152f
9153t
9154t9154f9153f
9154t9154f9150f
9151t
9152t
9153t
...

What's important to notice is:

  1. The finally block is executed even in the case of a StackOverflowError.
  2. A program that has run into a StackOverflowError can be in an unpredictable state. Here the only observable effect is the fact that println doesn't output a line break. In a more complex program this could mean that you can't trust the state of anything that the program had been working on, and that the safest thing to do is to bail out completely.
Amphetamine answered 24/7, 2013 at 14:42 Comment(0)
N
1

You will get the same error when you call asd in the finally block - you need to let the asd frames on the stack return / pop in order to resolve the error

Norbertonorbie answered 24/7, 2013 at 13:57 Comment(0)
S
1

But how is the recursive function in the finally block being executed as we already facing a StackOverflowError?

If you try to handle a StackOverflowError, it will only result in further, subsequent StackOverflowErrors, since you are trying to do further execution on an already full stack. You should not attempt to catch, use this information to structure your code better. This is the point of an unchecked exception in java. If you are unfamiliar with the different kinds of exceptions...

Checked Exceptions

Checked exceptions are exceptions are checked at compile time and represent conditions that need to be handled (via a try-catch statement) since it is out of the program's control. For example, if you want to call Thread.sleep() anywhere inside of a given thread, you will be required to handle a potential InterruptedException that could occur if the thread is inturrupted by a different thread. Since the possiblitiy of this occurring is known at compile time, you as the programmer are requried to handle this case.

Unchecked Exceptions

The java.lang.Throwable class states...

For the purposes of compile-time checking of exceptions, Throwable and any subclass of Throwable that is not also a subclass of either RuntimeException or Error are regarded as checked exceptions.

Since StackOverflowError is a subclass of Error, it is not regarded as a checked exception, and is therefore "unchecked". Unchecked exceptions often arise due to programming errors, e.g. in your case, the incorrect termination of an infinite recursion. Other unchecked exceptions could arise if you try to access some member of a null variable or an invalid index in an array. These exceptions are near impossible to check at compile time and are more often used to show the programmer a fault in his or her code.

Further Reading

Java Exceptions

Checked vs Unchecked Exceptions

Shandeigh answered 24/7, 2013 at 14:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.