system.exit会立即终止jvm且不执行finally和shutdown hook以外的清理逻辑,仅用于启动失败等极端场景;exit code应为0–127,避免负数和143;runtime.halt更暴力,跳过所有清理。

System.exit 会立即终止 JVM,连 finally 都不执行
它不是“退出当前方法”,而是直接杀掉整个 JVM 进程。哪怕你写在 try 块里,对应的 finally 也不会运行——这点和 return 完全不同。
常见错误现象:System.exit(0) 后数据库连接没关闭、日志缓冲区没 flush、临时文件没清理,导致资源泄漏或数据不一致。
- 只在真正需要强制终止时用,比如启动失败、配置严重错误、安全策略触发
- Web 应用(Spring Boot 等)中绝对不要在业务逻辑里调用
System.exit,容器会自己管理生命周期 - 测试代码里慎用:JUnit 5 的
@AfterEach不会执行,可能影响后续测试
exit code 传 0 和非 0 的实际含义
操作系统靠这个数字判断程序是否“正常结束”。0 表示成功,非 0(通常 1–127)表示某种失败类型——但具体数值含义得你自己定义并文档化,Java 不做解释。
使用场景:Shell 脚本依赖 $? 判断 Java 程序结果,比如:
立即学习“Java免费学习笔记(深入)”;
java MyApp && echo "OK" || echo "Failed"
如果 MyApp 里写了 System.exit(1),那 echo "Failed" 就会执行。
- 避免用负数:某些 Unix 系统会把负数转成无符号字节,
System.exit(-1)可能变成 255 - 别滥用 1:建议按错误类型分码,比如
System.exit(2)表示参数错误,System.exit(3)表示文件不可读 - 注意信号冲突:值 143 是 SIGTERM 常见退出码,别手动设成这个,否则和 kill -15 混淆
替代方案:用 Runtime.halt 更暴力,但更危险
Runtime.getRuntime().halt(int) 和 System.exit 类似,但它连 shutdown hook 都跳过,JVM 瞬间消失。一般只用于极端情况,比如检测到内存被破坏、JVM 内部状态已不可信。
性能/兼容性影响:它绕过所有 Java 层清理逻辑,比 System.exit 更快,但也更不可控。OpenJDK 和 HotSpot 行为一致,但 GraalVM Native Image 中行为可能受限。
- 永远不要在生产代码里写
halt,连测试都尽量避开 - 它不会触发
SecurityManager.checkExit,所以权限检查失效(如果你还用这玩意儿的话) - 调试时很难捕获:IDE 断点、JFR 事件、GC 日志都可能戛然而止
进程没退出?可能是 shutdown hook 卡住了
调用 System.exit 后,JVM 会运行所有已注册的 shutdown hook 线程,等它们全部结束才真正退出。如果某个 hook 里有死循环、阻塞 IO 或等待锁,进程就挂在那里不动。
常见错误现象:Java 进程 ps 还在,CPU 为 0,jstack 显示线程停在 java.lang.Shutdown$Lock.wait。
- hook 里别做耗时操作:不能发 HTTP 请求、不能等数据库响应、不能 sleep
- 给 hook 加超时:用
Thread.join(3000)主动放弃等待 - 避免在 hook 里再调用
System.exit,会引发IllegalStateException
真正难处理的是那些隐式注册了 hook 的库——比如某些监控 SDK、日志框架、连接池,它们可能悄悄加了 hook 却没提供关闭入口。这时候得看文档,或者用 jstack 抓现场确认谁在 hold 住退出流程。










