Shutdown Hook 是进程最后一次做清理的机会,但它不是保证。这篇用真实信号与退出路径量出:什么情况下运行、运行时有哪些限制、被强杀时清理做到了哪里,以及多个 hook 之间没有顺序。
main 正常返回 → 退出码 0;hook 运行完了 System.exit(3) → 退出码 3;hook 运行完了 main 抛出未捕获异常 → 退出码 1;hook 运行完了 Runtime.halt(4) → 退出码 4;hook 没有运行 SIGINT(Ctrl-C) → 退出码 130;hook 运行完了 SIGTERM(kill) → 退出码 143;hook 运行完了 SIGHUP(终端关闭) → 退出码 129;hook 运行完了 SIGQUIT(kill -3,线程转储) → 进程仍然活着(被我杀掉);hook 没有运行 SIGKILL(kill -9) → 被信号 9 杀死(SIGKILL);hook 没有运行
第 1 次:启动顺序 4 → 3 → 5 → 1 → 2;各 hook 开始时刻的跨度 1 ms;线程名 extra-1,extra-2,extra-3,extra-4,extra-5 第 2 次:启动顺序 3 → 5 → 1 → 4 → 2;各 hook 开始时刻的跨度 0 ms;线程名 extra-1,extra-2,extra-3,extra-4,extra-5 第 3 次:启动顺序 4 → 2 → 5 → 1 → 3;各 hook 开始时刻的跨度 0 ms;线程名 extra-1,extra-2,extra-3,extra-4,extra-5
被信号 9 杀死(SIGKILL);hook 完成的清理步骤 3 / 10;最后一条:[ 1765 ms] 清理步骤 3;hook 是否结束 = False 对照:不 kill -9:退出码 143,用时 4.0 s,清理步骤 10 / 10,hook 是否结束 = True hook 里 sleep 30 秒:SIGTERM 后 3 秒,进程仍然活着;hook 日志 ['[ 824 ms] hook 开始,线程 my-hook']
hook 里再 addShutdownHook:关机期间 addShutdownHook 抛出 IllegalStateException:Shutdown in progress hook 里调用 System.exit(9):进程仍然活着(死锁,被我杀掉);日志 hook 开始,线程 my-hook | hook 里调用 System.exit(9) hook 里调用 Runtime.halt(9):退出码 9;日志 hook 开始,线程 my-hook | hook 里调用 Runtime.halt(9) daemon 线程在 hook 运行期间(823–1528 ms):daemon 线程仍然写了 2 条日志(没有被停掉)
退出码 0,用时 1.6 s;日志 [ 7 ms] main 返回,非 daemon 线程还在跑 | [ 1509 ms] 非 daemon 线程结束 | [ 1514 ms] hook 开始,线程 my-hook | [ 1515 ms] hook 结束