Why kill -9 Can Corrupt Your Data and How to Gracefully Shut Down Spring Boot

The article explains the dangers of using kill -9 to terminate Java processes, illustrates how it can cause data loss in database operations, and provides several practical approaches—including SIGTERM, ConfigurableApplicationContext.close(), Spring Boot Actuator, and a custom Tomcat shutdown hook—to gracefully stop Spring Boot services while preserving data integrity.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Why kill -9 Can Corrupt Your Data and How to Gracefully Shut Down Spring Boot

Background: kill -9 and Its Risks

The Linux kill command sends signals to processes; the default is SIGTERM (15). When a process does not stop, SIGKILL (9) is often used. Because SIGKILL forcefully terminates a process, it is equivalent to a sudden power outage and can leave databases in an inconsistent state, especially with non‑transactional storage engines such as MyISAM.

Problem: Data Inconsistency When Using kill -9

In a money‑transfer scenario, if a process handling debit and credit operations is killed with SIGKILL, the debit may be persisted while the credit is lost, leading to unrecoverable financial errors. Distributed services face similar risks, making SIGKILL unsuitable for production shutdowns.

Graceful Shutdown Strategies

1. Using SIGTERM (kill -15)

Sending SIGTERM allows the JVM to interrupt running threads, giving them a chance to finish cleanly. The following Spring Boot controller demonstrates the effect:

@GetMapping("/test")
public String test(){
    log.info("test --- start");
    try {
        Thread.sleep(100000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    log.info("test --- end");
    return "test";
}

Steps:

Start the application (e.g., sudo mvn spring-boot:run).

Find the process ID with sudo ps -ef | grep shutdown.

Trigger the endpoint ( curl 127.0.0.1:9988/test) to put the thread to sleep.

Terminate with sudo kill -15 <pid>.

The log shows an InterruptedException from the sleeping thread, followed by normal shutdown messages, confirming that the thread was allowed to finish.

2. Closing the Application Context Programmatically

Implement ApplicationContextAware and invoke ConfigurableApplicationContext.close():

package com.ymy.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Slf4j
public class TestController implements ApplicationContextAware {
    private ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context = applicationContext;
    }

    @GetMapping("/test")
    public String test(){
        log.info("test --- start");
        try { Thread.sleep(100000); } catch (InterruptedException e) { e.printStackTrace(); }
        log.info("test --- end");
        return "test";
    }

    @PostMapping("/shutdown")
    public void shutdown(){
        ConfigurableApplicationContext ctx = (ConfigurableApplicationContext) context;
        ctx.close();
    }
}

The close() method removes the JVM shutdown hook and triggers a graceful termination of the Spring context.

3. Using Spring Boot Actuator Shutdown Endpoint

Add the actuator dependency and enable the shutdown endpoint:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
server:
  port: 9988
management:
  endpoints:
    web:
      exposure:
        include: shutdown
  endpoint:
    shutdown:
      enabled: true

Calling curl -X POST http://localhost:8888/actuator/shutdown returns a friendly message and stops the service after completing in‑flight requests.

4. Custom Elegant Shutdown with Tomcat

Define a TomcatConnectorCustomizer that pauses the connector and waits for thread‑pool termination:

package com.ymy.config;

import org.apache.catalina.connector.Connector;
import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ElegantShutdownConfig implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> {
    private volatile Connector connector;
    private final int waitTime = 10; // seconds

    @Override
    public void customize(Connector connector) {
        this.connector = connector;
    }

    @Override
    public void onApplicationEvent(ContextClosedEvent event) {
        connector.pause();
        Executor executor = connector.getProtocolHandler().getExecutor();
        if (executor instanceof ThreadPoolExecutor) {
            ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
            threadPoolExecutor.shutdown();
            try {
                if (!threadPoolExecutor.awaitTermination(waitTime, TimeUnit.SECONDS)) {
                    System.out.println("Please consider forceful shutdown");
                }
            } catch (InterruptedException ex) {
                System.out.println("Interrupted during shutdown");
                Thread.currentThread().interrupt();
            }
        }
    }
}

Register the config as a bean and customize the embedded Tomcat factory:

package com.ymy;

import com.ymy.config.ElegantShutdownConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class ShutdownServerApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext ctx = SpringApplication.run(ShutdownServerApplication.class, args);
        ctx.registerShutdownHook();
    }

    @Bean
    public ElegantShutdownConfig elegantShutdownConfig() {
        return new ElegantShutdownConfig();
    }

    @Bean
    public ServletWebServerFactory servletContainer() {
        TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
        tomcat.addConnectorCustomizers(elegantShutdownConfig());
        return tomcat;
    }
}

When the application receives a shutdown signal, it pauses new requests, waits up to waitTime seconds for active tasks to finish, and then stops without throwing exceptions.

5. Executing Cleanup Tasks on Shutdown

Use the @PreDestroy annotation to run code before the container stops, for example to back up data:

package com.ymy.config;

import org.springframework.context.annotation.Configuration;
import javax.annotation.PreDestroy;

@Configuration
public class DataBackupConfig {
    @PreDestroy
    public void backData(){
        System.out.println("Backing up data before shutdown...");
    }
}

This method is invoked automatically during the graceful shutdown sequence, allowing developers to perform finalization tasks such as data backup or logging.

Conclusion

Using kill -9 on a Java service can cause abrupt termination, data loss, and hard‑to‑debug errors. Instead, adopt one of the graceful shutdown techniques described above—SIGTERM, programmatic context closing, Actuator’s shutdown endpoint, or a custom Tomcat shutdown hook—to ensure in‑flight requests complete, resources are released cleanly, and optional cleanup logic runs safely.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Javaspring-bootGraceful ShutdownActuatorSIGTERMThread.interruptLinux kill
Code Ape Tech Column
Written by

Code Ape Tech Column

Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn

0 followers
Reader feedback

How this landed with the community

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.