如何调试导致Helidon服务器突然关闭的SIGTERM问题
问题背景
简单Java程序收到kill -SIGTERM信号时,会触发JVM关闭钩子(Runtime.addShutdownHook()),钩子执行完成后JVM可正常退出。
简单Helidon服务器自带JVM关闭钩子,能保证CDI正常关闭、Netty停止监听,@BeforeDestroyed、@Destroyed注解逻辑和关闭钩子仅输出日志,最终服务器与JVM都能干净退出。
但我维护的一个复杂Helidon服务器出现异常:
- 当通过监控特定删除文件触发关闭流程时,能看到Netty关闭、
@BeforeDestroyed/@Destroyed执行、关闭钩子日志,服务器和JVM干净退出,耗时1.011秒。 - 但发送
kill -SIGTERM信号后,服务器瞬间退出(数毫秒内),Netty未关闭,上述关闭逻辑和钩子都未执行。
该问题在Windows(使用Cygwin的kill命令)和Linux环境下均可复现。
调试方法
- 显式注册信号处理器并打日志:通过
Signal.handle(new Signal("TERM"), handler)注册自定义SIGTERM处理器,在处理器中输出详细日志,确认信号是否被接收、处理器执行是否被中断。 - 检查JVM退出状态码:Linux下执行
echo $?,Windows下查看进程退出码:143对应正常SIGTERM触发退出,非该值则可能是代码直接调用System.exit()导致的强制退出。 - 启用JVM退出诊断日志:启动时添加JVM参数
-XX:+PrintExitFlags、-XX:+PrintCommandLineFlags、-Xlog:exit*=debug,查看退出时的JVM状态标志,确认是正常关闭还是强制退出。 - 排查自定义信号拦截逻辑:检查代码中是否有其他
SIGTERM处理器,可能覆盖了Helidon默认钩子,或处理器内直接调用System.exit()跳过了关闭流程。 - 对比线程快照:发送
SIGTERM前几秒用jstack(Linux)/jstack.exe(Windows)获取线程dump,发送信号后立即再获取一次,对比线程状态,排查是否有线程死锁、阻塞或关键线程被意外终止。 - 排查第三方依赖:检查项目是否引入JNI等原生库,这类库可能直接捕获
SIGTERM并终止进程,导致JVM无法触发关闭钩子。
可能导致异常退出的情况
- 自定义处理器直接退出:代码中注册的
SIGTERM处理器内调用了System.exit(int),跳过JVM关闭钩子执行流程。 - 原生代码拦截信号:依赖的JNI或原生组件捕获
SIGTERM后直接终止进程,JVM无机会执行关闭逻辑。 - JVM致命错误:收到信号时JVM正处理OOM、内存损坏等致命错误,直接触发紧急退出,跳过关闭钩子。
- 关闭钩子被移除:代码中调用
Runtime.removeShutdownHook()移除了Helidon默认的关闭钩子,导致信号触发后无对应逻辑执行。 - 进程处于不可中断状态:进程因IO阻塞等原因处于操作系统不可中断状态,收到信号后被系统强制终止。
内容的提问来源于stack exchange,提问作者Nathan
相关产品推荐
相关产品推荐

