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:

Java Program to Implement Hash Tables Chaining with Binary Trees
Java Program to Implement LinkedHashMap API
JUnit5 @RunWith
Spring Boot - Batch Service
Spring Data MongoDB – Indexes, Annotations and Converters
Introduction to Spliterator in Java
Guide to Java 8’s Collectors
Java Program to add two large numbers using Linked List
So sánh HashMap và Hashtable trong Java
Transaction Propagation and Isolation in Spring @Transactional
The DAO with Spring and Hibernate
Java Program to Implement Wagner and Fisher Algorithm for online String Matching
Vấn đề Nhà sản xuất (Producer) – Người tiêu dùng (Consumer) và đồng bộ hóa các luồng trong Java
ExecutorService – Waiting for Threads to Finish
LIKE Queries in Spring JPA Repositories
Binary Numbers in Java
HTTP Authentification and CGI/Servlet
Java Copy Constructor
Spring Web Annotations
JUnit5 Programmatic Extension Registration with @RegisterExtension
Java Program to Check whether Graph is a Bipartite using DFS
Java Program to Find Maximum Element in an Array using Binary Search
Java Program to Generate a Random Subset by Coin Flipping
Java Program to Implement TreeSet API
Truyền giá trị và tham chiếu trong java
Java Program to Create a Random Graph Using Random Edge Generation
Java Program to Implement Threaded Binary Tree
“Stream has already been operated upon or closed” Exception in Java
Java Program to Represent Graph Using Adjacency Matrix
Java Program to Check Whether an Input Binary Tree is the Sub Tree of the Binary Tree
Overview of the java.util.concurrent
Sort a HashMap in Java