Adding Shutdown Hooks for JVM Applications

1. Overview

It’s typically easy to start-up a service. However, sometimes we need to have a plan for gracefully shutting one down.

In this tutorial, we’re going to take a look at different ways a JVM application can terminate. Then, we’ll use Java APIs to manage JVM shutdown hooks. Please refer to this article to learn more about shutting down the JVM in Java applications.

2. JVM Shutdown

The JVM can be shut down in two different ways:

  1. A controlled process
  2. An abrupt manner

A controlled process shuts down the JVM when either:

  • The last non-daemon thread terminates. For example, when the main thread exits, the JVM starts its shutdown process
  • Sending an interrupt signal from the OS. For instance, by pressing Ctrl + C or logging off the OS
  • Calling System.exit() from Java code

While we all strive for graceful shutdowns, sometimes the JVM may shut down in an abrupt and unexpected manner. The JVM shuts down abruptly when:

  • Sending a kill signal from the OS. For example, by issuing a kill -9 <jvm_pid>
  • Calling Runtime.getRuntime().halt() from Java code
  • The host OS dies unexpectedly, for example, in a power failure or OS panic

3. Shutdown Hooks

The JVM allows registering functions to run before it completes its shutdown. These functions are usually a good place for releasing resources or other similar house-keeping tasks. In JVM terminology, these functions are called shutdown hooks.

Shutdown hooks are basically initialized but unstarted threads. When the JVM begins its shutdown process, it will start all registered hooks in an unspecified order. After running all hooks, the JVM will halt.

3.1. Adding Hooks

In order to add a shutdown hook, we can use the Runtime.getRuntime().addShutdownHook() method:

Thread printingHook = new Thread(() -> System.out.println("In the middle of a shutdown"));
Runtime.getRuntime().addShutdownHook(printingHook);

Here we simply print something to the standard output before JVM shuts down itself. If we shut down the JVM like following:

> System.exit(129);
In the middle of a shutdown

Then we’ll see that the hook actually prints the message to standard output.

The JVM is responsible for starting hook threads. Therefore, if the given hook has been already started, Java will throw an exception:

Thread longRunningHook = new Thread(() -> {
    try {
        Thread.sleep(300);
    } catch (InterruptedException ignored) {}
});
longRunningHook.start();

assertThatThrownBy(() -> Runtime.getRuntime().addShutdownHook(longRunningHook))
  .isInstanceOf(IllegalArgumentException.class)
  .hasMessage("Hook already running");

Obviously, we also can’t register a hook multiple times:

Thread unfortunateHook = new Thread(() -> {});
Runtime.getRuntime().addShutdownHook(unfortunateHook);

assertThatThrownBy(() -> Runtime.getRuntime().addShutdownHook(unfortunateHook))
  .isInstanceOf(IllegalArgumentException.class)
  .hasMessage("Hook previously registered");

3.2. Removing Hooks

Java provides a twin remove method to remove a particular shutdown hook after registering it:

Thread willNotRun = new Thread(() -> System.out.println("Won't run!"));
Runtime.getRuntime().addShutdownHook(willNotRun);

assertThat(Runtime.getRuntime().removeShutdownHook(willNotRun)).isTrue();

The removeShutdownHook() method returns true when the shutdown hook is successfully removed.

3.3. Caveats

The JVM runs shutdown hooks only in case of normal terminations. So, when an external force kills the JVM process abruptly, the JVM won’t get a chance to execute shutdown hooks. Additionally, halting the JVM from Java code will also have the same effect:

Thread haltedHook = new Thread(() -> System.out.println("Halted abruptly"));
Runtime.getRuntime().addShutdownHook(haltedHook);
        
Runtime.getRuntime().halt(129);

The halt method forcibly terminates the currently running JVM. Therefore, registered shutdown hooks won’t get a chance to execute.

4. Conclusion

In this tutorial, we looked at different ways a JVM application can possibly terminate. Then, we used a few runtime APIs to register and de-register shutdown hooks.

As usual, the sample code is available over on GitHub.

Related posts:

Jackson Annotation Examples
Implementing a Runnable vs Extending a Thread
Guide to the Java Queue Interface
Java Program to Perform Inorder Recursive Traversal of a Given Binary Tree
Spring Data JPA @Query
Guide to the Fork/Join Framework in Java
Java Program to Implement ArrayDeque API
Java Program to Search for an Element in a Binary Search Tree
Testing an OAuth Secured API with Spring MVC
String Operations with Java Streams
Hướng dẫn Java Design Pattern – Memento
Java Program to Implement ScapeGoat Tree
Java Program to Emulate N Dice Roller
Java Program to Implement Hash Tree
Java Program to Check Whether an Undirected Graph Contains a Eulerian Path
Java Program to Create a Minimal Set of All Edges Whose Addition will Convert it to a Strongly Conne...
Java Program to Delete a Particular Node in a Tree Without Using Recursion
Java Program to Perform Inorder Non-Recursive Traversal of a Given Binary Tree
Handling Errors in Spring WebFlux
An Introduction to ThreadLocal in Java
Java Program to Describe the Representation of Graph using Incidence List
Guide to CopyOnWriteArrayList
Send an email using the SMTP protocol
Guide to Spring @Autowired
Hướng dẫn Java Design Pattern – Command
Java Program to Convert a Decimal Number to Binary Number using Stacks
Exception Handling in Java
The XOR Operator in Java
Spring Boot Tutorial – Bootstrap a Simple Application
Java Program to Find the Longest Subsequence Common to All Sequences in a Set of Sequences
Phương thức forEach() trong java 8
Java – Convert File to InputStream