Ensuring Safe JVM Shutdown with Shutdown Hooks and Signal Handling
To prevent loss of asynchronous tasks and data corruption during application restarts, this article explains how to safely shut down the JVM using shutdown hooks, custom signal handling, and proper hook implementation, while highlighting risks of forced termination and best‑practice considerations.
Background
User: All goods have arrived, why are there still items in the cart?\nProduct: Users report that after order submission the cart is not cleared.\nR&D: Investigating… after a restart the asynchronous tasks were lost.\nProduct: Is it acceptable to lose tasks on restart?\nR&D: …
In order‑heavy transaction flows, many operations such as updating recent addresses, sending notifications via MQ, and clearing carts are performed asynchronously to improve response time, but this introduces a risk: if the JVM restarts before those asynchronous tasks finish, they may be lost.
JVM Shutdown
First, understand the situations that cause a JVM to shut down. The diagram below (original image) shows normal shutdown, abnormal shutdown, and forced termination (e.g., kill -9, Runtime.halt(), power loss). Forced termination gives the JVM no chance to run any cleanup code, which can lead to data loss, file corruption, task loss, or duplicate updates.
For normal or abnormal shutdowns, the JVM invokes all registered shutdown hooks before exiting. Therefore, we recommend using System.exit(0) for graceful termination instead of forceful methods.
JVM Safe Exit
For Tomcat‑based web applications, you can register a custom hook with Runtime.addShutdownHook(Thread hook) . For worker‑type applications, a signal‑based mechanism is preferred.
Signal‑Based Process Notification
Signals are an OS‑level asynchronous communication mechanism. Linux provides several termination signals (e.g., SIGKILL, SIGTERM, SIGINT). Windows has its own set (e.g., SIGINT, SIGTERM, SIGBREAK).
Signal Name
Purpose
SIGKILL
Forcefully terminate the process
SIGTERM
Software termination signal
SIGTSTP
Stop process from terminal
SIGPROF
Timer for profiling
SIGUSR1
User‑defined signal 1
SIGUSR2
User‑defined signal 2
SIGINT
Interrupt process (Ctrl+C)
SIGQUIT
Generate core dump and terminate
Windows equivalents:
Signal Name
Purpose
SIGINT
Ctrl+C interrupt
SIGTERM
Software termination (kill)
SIGBREAK
Ctrl+Break interrupt
We choose SIGUSR2 for Linux and SIGINT for Windows as custom signals that do not interfere with normal operations.
Safe Exit Implementation
Step 1 – Initialize a Signal instance:
Signal sig = new Signal(getOSSignalType());Step 2 – Determine the appropriate signal name based on the OS:
private String getOSSignalType(){
return System.getProperties().getProperty("os.name")
.toLowerCase().startsWith("win") ? "INT" : "USR2";
}Step 3 – Register the handler with the JVM:
Signal.handle(sig, shutdownHandler);Step 4 – Implement the handler that registers a shutdown‑hook thread:
public class ShutdownHandler implements SignalHandler {
/**
* Process the received signal
* @param signal the signal
*/
public void handle(Signal signal) {
// logic to register hook
}
}Step 5 – Register the actual shutdown hook:
private void registerShutdownHook(){
Thread t = new Thread(new ShutdownHook(), "ShutdownHook-Thread");
Runtime.getRuntime().addShutdownHook(t);
}Step 6 – Trigger JVM exit after handling the signal:
Runtime.getRuntime().exit(0);Step 7 – Define the work performed by the hook (e.g., cleanup, waiting for pending tasks):
class ShutdownHook implements Runnable{
@Override
public void run() {
System.out.println("ShutdownHook execute start...");
try {
TimeUnit.SECONDS.sleep(10); // simulate cleanup work
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("ShutdownHook execute end...");
}
}A timeout (e.g., 30 seconds) can be added; if cleanup does not finish, the stop script may fall back to kill -9 .
Precautions When Using Shutdown Hooks
Hooks run concurrently; their execution order is not guaranteed. Consolidate related cleanup steps into a single hook to avoid race conditions or deadlocks.
Keep hook execution short; avoid heavy computation or blocking I/O that would delay JVM termination.
During OS shutdown, the hook may be abruptly terminated if the system forces the process to exit.
Do not register or deregister hooks from within a hook; doing so throws IllegalStateException .
Avoid calling System.exit() inside a hook (it can block shutdown); you may use Runtime.halt() if necessary.
Uncaught exceptions in a hook are handled by the thread’s default exception handler and do not affect other hooks or the JVM exit.
Conclusion
By employing shutdown hooks and custom signal handling, applications can ensure that asynchronous operations complete and resources are cleaned up before the JVM terminates, thereby avoiding data loss, file corruption, and other issues caused by forced termination.
For more articles from Xindada Technology, follow their public account.
Dada Group Technology
Sharing insights and experiences from Dada Group's R&D department on product refinement and technology advancement, connecting with fellow geeks to exchange ideas and grow together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.