There are several ways an application can be terminated (and the JVM stopped) programmatically:
- Normal termination without an error status code
- Abnormal termination because of an unhandled exception or a forced programmatic exit with or without an error status code
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.
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:
- If there are no other threads, the JVM stops executing and returns an error code and the stack trace
- If the thread with an unhandled exception was not the main one, other threads (if present) continue running
- If the main thread has thrown an unhandled exception and the child threads (if present) are daemon, they exit too
- If there is at least one user child thread, the JVM continues running until all user threads exit
There are also ways to programmatically force the application to stop:
- System.exit(0);
- Runtime.getRuntime().exit(0);
- Runtime.getRuntime().halt(0);
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):
- Zero indicates normal termination
- The nonzero value indicates abnormal termination
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);
}
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.