Recently I was asked this question; "Is finally guaranteed to be always called in Java programs". "What are the cases when finally will not be called".
And the output is as below.
The next bit is when there is an exception in the normal flow of try block.
And the output is as below.
And the output is as below.
And the output is as below.
My first answer was it will always be called, of course, that was a simplistic answer and once I thought about it for a while some more thoughts came to my mind.
- In a healthy VM, finally is guaranteed to be called always. At least this is what I thought.
- If somebody abruptly terminates the node the JVM really will have no time to call finally
- If somebody kills the JVM with a SIGKILL, the JVM will have no time to call finally.
Now I started thinking about, is there a scenario when finally will not be called even if conditions 2 & 3 did not occur.
One of the calls that comes to mind is System.exit(), its behavior is very similar to abruptly terminate a JVM so I was keen to understand if it will call finally. As I looked at the finally specification, I found the following statement.
The finally block always executes when the try block exits. This ensures that the finally block is executed even if an unexpected exception occurs.When one looks at System.exit(), the following information is found.
Terminates the currently running Java Virtual Machine. The argument serves as a status code; by convention, a nonzero status code indicates abnormal termination. This method calls the exit method in class Runtime. This method never returns normally.The key point here is that System.exit() never returns. If it is never returned then the try block will get no chance of getting exited. If try block is not getting exited, it the JVM can't call finally. Finally, as they say, proof of the pudding is in eating so I decided to try it with the latest VM.
public class FinallyCode { public static void main(String[] args) { try { System.out.println("Try is terminating normally."); } finally { System.out.println("Finally is called."); } } }
And the output is as below.
Try is terminating normally. Finally is called.As we can see, this is the expected behavior, once the normal flow of try block is complete, the code in finally is called.
The next bit is when there is an exception in the normal flow of try block.
public class FinallyCode { public static void main(String[] args) { try { System.out.println("Try is terminating after throwing NullPointerException."); throw new NullPointerException(); } finally { System.out.println("Finally is called."); } } }
And the output is as below.
Try is terminating after throwing NullPointerException. Finally is called. Exception in thread "main" java.lang.NullPointerException at FinallyCode.main(FinallyCode.java:6)We can see that when an exception is thrown in the flow of try block, the finally is called on the try block exit.
public class FinallyCode { public static void main(String[] args) { try { System.out.println("Try is terminating after calling System.exit()."); System.exit(0);; } finally { System.out.println("Finally is called."); } } }
And the output is as below.
Try is terminating after calling System.exit().Just to be doubly sure, I tried with the error code in System.exit as another number apart from 0.
public class FinallyCode { public static void main(String[] args) { try { System.out.println("Try is terminating after calling System.exit()."); System.exit(1);; } finally { System.out.println("Finally is called."); } } }
And the output is as below.
Try is terminating after calling System.exit().That leaves us to a small matter of what one needs to do when System.exit is called. Java defines a shutdown hook that can call a user-defined function in case VM is terminating.
A shutdown hook is simply an initialized but unstarted thread. When the virtual machine begins its shutdown sequence it will start all registered shutdown hooks in some unspecified order and let them run concurrently. When all the hooks have finished it will then run all uninvoked finalizers if finalization-on-exit has been enabled. Finally, the virtual machine will halt. Note that daemon threads will continue to run during the shutdown sequence, as will non-daemon threads if shutdown was initiated by invoking the exit method.
Once the shutdown sequence has begun it can be stopped only by invoking the halt method, which forcibly terminates the virtual machine.
Once the shutdown sequence has begun it is impossible to register a new shutdown hook or de-register a previously-registered hook. Attempting either of these operations will cause an IllegalStateException to be thrown.
So, how do we define a shutdown hook? Look at the code below.
public class FinallyCode { public static void main(String[] args) { try { Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { System.out.println("Shutdown hook is called."); } }); System.out.println("Try is terminating after calling System.exit()."); System.exit(5);; } finally { System.out.println("Finally is called."); } } }The output is as below.
Try is terminating after calling System.exit(). Shutdown hook is called.
So the learnings from this small experiment are as follows.
- When the virtual machine is working normally, the finally is guaranteed to be called unless System.exit is called in the normal flow of the execution.
- When System.exit is called, since the function never returns, finally is not called.
- If the JVM is terminated through SIGKILL or through hardware reset, the finally is not called.