Backend Development 11 min read

Graceful Shutdown of Spring Boot Applications Using JVM Signals and Actuator

The article explains how to achieve graceful shutdown of Spring Boot services by handling Linux kill signals, registering JVM shutdown hooks, and exposing an Actuator REST endpoint that safely releases resources, stops beans, and terminates the JVM without data loss.

Java Captain
Java Captain
Java Captain
Graceful Shutdown of Spring Boot Applications Using JVM Signals and Actuator

In many business systems that run Spring Boot with an embedded Tomcat, administrators often use the Linux kill command to stop processes, but a proper graceful shutdown requires careful handling.

What is graceful shutdown

Graceful shutdown means ensuring that when an application stops, it notifies processes to release resources.

Thread pools: shutdown() (no new tasks, wait) or shutdownNow() (calls Thread.interrupt ).

Socket connections such as Netty or MQ.

Notify registration centers for quick deregistration, e.g., Eureka.

Clean temporary files, e.g., POI.

Release various heap and off‑heap memory.

Forcefully terminating a process can cause data loss, unrecoverable state, or data inconsistency in distributed environments.

kill command

kill -9 pid simulates a crash, while kill -15 pid sends SIGTERM, allowing the application to shut down gracefully; sometimes the application may still fail to stop.

# View JVM process pid
jps
# List all signal names
kill -l
# Windows signal constants
# INT  SIGINT     2   Ctrl+C interrupt
# ILL  SIGILL     4   Illegal instruction
# FPE  SIGFPE     8   floating point exception
# SEGV SIGSEGV   11   segment violation
# TERM SIGTERM    5   Software termination signal from kill
# BREAK SIGBREAK 21   Ctrl‑Break sequence
# ABRT SIGABRT   22   abnormal termination triggered by abort call
# Linux signals
# HUP  SIGHUP      1   terminal hangup
# INT  SIGINT      2   interrupt (Ctrl+C)
# QUIT SIGQUIT     3   quit (Ctrl+\)
# KILL SIGKILL     9   forced termination
# TERM SIGTERM    15   termination
# CONT SIGCONT    18   continue (opposite of STOP)
# STOP SIGSTOP   19   pause (Ctrl+Z)
# ...

JVM handles Linux signals via a custom SignalHandler loaded at startup; when the JVM shuts down, the corresponding handler is invoked.

public interface SignalHandler {
    SignalHandler SIG_DFL = new NativeSignalHandler(0L);
    SignalHandler SIG_IGN = new NativeSignalHandler(1L);
    void handle(Signal var1);
}
class Terminator {
    private static SignalHandler handler = null;
    static void setup() {
        if (handler == null) {
            SignalHandler var0 = new SignalHandler() {
                public void handle(Signal var1) {
                    Shutdown.exit(var1.getNumber() + 128);
                }
            };
            handler = var0;
            try {
                Signal.handle(new Signal("INT"), var0);
            } catch (IllegalArgumentException e) {}
            try {
                Signal.handle(new Signal("TERM"), var0);
            } catch (IllegalArgumentException e) {}
        }
    }
}

Runtime.addShutdownHook registers a hook that runs when the JVM exits:

public class Runtime {
    public void addShutdownHook(Thread hook) {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new RuntimePermission("shutdownHooks"));
        }
        ApplicationShutdownHooks.add(hook);
    }
}
class ApplicationShutdownHooks {
    private static IdentityHashMap
hooks;
    static synchronized void add(Thread hook) {
        if (hooks == null) throw new IllegalStateException("Shutdown in progress");
        if (hook.isAlive()) throw new IllegalArgumentException("Hook already running");
        if (hooks.containsKey(hook)) throw new IllegalArgumentException("Hook previously registered");
        hooks.put(hook, hook);
    }
    // ... execution of hooks, finalizers, halt, etc.
}

Spring 3.2.12 uses ContextClosedEvent and LifecycleProcessor.onClose() to stop beans during shutdown.

public abstract class AbstractApplicationContext extends DefaultResourceLoader {
    public void registerShutdownHook() {
        if (this.shutdownHook == null) {
            this.shutdownHook = new Thread() {
                @Override public void run() { doClose(); }
            };
            Runtime.getRuntime().addShutdownHook(this.shutdownHook);
        }
    }
    protected void doClose() {
        // publish ContextClosedEvent, stop Lifecycle beans, destroy beans, close BeanFactory, etc.
    }
}

Spring Boot provides a REST endpoint via spring-boot-starter-actuator to trigger graceful shutdown:

# Enable shutdown endpoint
endpoints.shutdown.enabled=true
# Disable password protection
endpoints.shutdown.sensitive=false
management.context-path=/manage
management.port=8088
management.address=127.0.0.1
# Enable security if needed
endpoints.shutdown.sensitive=true
security.user.name=admin
security.user.password=secret
management.security.role=SUPERUSER

Calling POST /shutdown invokes ShutdownEndpoint , which ultimately calls AbstractApplicationContext.close() to run all shutdown hooks, stop beans, and halt the JVM.

JavaJVMSpring BootGraceful ShutdownActuator
Java Captain
Written by

Java Captain

Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.