Application termination

There are several ways an application can be terminated (and the JVM stopped) programmatically:

If there are no exceptions and infinite loops, the main(String[]) method completes with a return statement or after its last statement is executed. As soon as it happens, the main application thread returns the control flow to the JVM and the JVM stops executing, too.

That is the happy ending, and many applications enjoy it in real-life. Most of our examples, except those when we have demonstrated exceptions or infinite loops, have ended successfully, too.

However, there are other ways a Java application can exit, some of them quite graceful too. Others – not as much.

If the main application thread created child threads or, in other words, a programmer has written code that generates other threads, even the graceful exit may not be as easy. It all depends on the kind of the child threads created. If any of them is a user thread (the default), then the JVM instance continues to run even after the main thread exits.

Only after all user threads are completed does the JVM instance stop. It is possible for the main thread to request that the child user thread complete (we will talk about this in the following section, Threads). But until it exits, the JVM continues running, and this means that the application is still running too.

But if all child threads are daemon threads (see the following section, Threads) or there are no child threads running, the JVM instance stops running as soon as the main application thread exits.

Without forced termination, the JVM instance continues running until the main application thread and all child user threads are completed. In the absence of child user threads or in the case where all the child threads are daemon, the JVM stops running as soon as the main application thread exits.

How the application exits in the case of an exception depends on the code design. We touched on it in the previous chapter while discussing the best practices of exception handling. If the thread captures all the exceptions in a try...catch block in main(String[]) or a similarly high-level method, then the control flow is returned back to the application code and it is up to the application (and the programmer who wrote the code) how to proceed – to try to recover, to log the error and continue, or to exit.

If, on the other hand, the exception remains unhandled and propagates into the JVM code, the thread (where the exception happened) stops executing and exits. Then, one of the following will occur:

There are also ways to programmatically force the application to stop:

All of the preceding methods force the JVM to stop executing any thread and exit with a status code passed in as the parameter (0, in our examples):

If the Java command was launched by some script or another system, the value of the status code can be used for the automation of the decision making about the next step. But that is already outside the application and Java code. 

The first two methods have identical functionality, because here is how System.exit() is implemented:

public static void exit(int status) {
Runtime.getRuntime().exit(status);
}
To see the source code in IDE, just click on the method.

The Java Virtual Machine exits when some thread invokes the exit() method of the Runtime or System class, or the halt() method of the Runtime class, and the exit or halt operation is permitted by the security manager.

The difference between exit() and halt() is that halt() forces JVM to exit immediately, while exit() performs additional actions that can be set using the Runtime.addShutdownHook() method.

But all these options are rarely used in mainstream programming, so we are already stepping way beyond the scope of this book.