System.exit() 是向 JVM 发出强制终止信号,其执行受 SecurityManager 权限控制;若策略未授权对应 exitVM.status,则抛出 SecurityException,JVM 不退出。

System.exit() 不是普通方法调用,而是向 JVM 发出强制终止信号。它的“安全性”不在于能否执行,而在于是否被允许执行——这取决于运行时是否存在安全管控机制,以及调用上下文是否合规。
安全管理器(SecurityManager)是核心校验入口
从 JDK 源码看,System.exit(int) 最终会委托 Runtime.exit(),而该方法第一件事就是检查 SecurityManager:
- 若 SecurityManager 存在,则调用其 checkExit(status) 方法进行权限判定
- 若检查失败(例如策略文件禁止 exit),则抛出 SecurityException,JVM 继续运行
- 若检查通过,才进入 Shutdown 流程;否则退出被拦截,程序可捕获异常并降级处理
注意:JDK 17+ 已移除 SecurityManager 的默认启用支持,但若显式配置(如通过 -Djava.security.manager=... 启用),它仍生效。生产环境极少启用,但嵌入式或沙箱场景(如插件平台)可能依赖它做出口控制。
安全策略(Policy)定义谁可以调用 exit
SecurityManager 的行为由安全策略文件(如 java.policy)驱动。典型配置示例如下:
grant {
permission java.lang.RuntimePermission "exitVM.0";
};
其中 exitVM.<status> 是细粒度权限,支持按状态码授权:
- exitVM.*:允许任意 status 退出
- exitVM.0:仅允许正常退出(status == 0)
- exitVM.1:仅允许特定错误码退出
这意味着:即使代码写了 System.exit(1),若策略未授予 exitVM.1 权限,也会被拒绝。这种机制可用于限制第三方模块只能“软失败”,不能强制中止整个进程。
权限缺失时的实际表现
没有权限 ≠ 静默失败。调用会被明确中断:
- 抛出 java.lang.SecurityException: checkExit.1(含具体 status)
- 异常沿调用栈向上传播,若未捕获,当前线程终止,但 JVM 不退出
- 主线程若因此结束且无其他非守护线程,JVM 才自然终止(返回码为 0 或未定义,取决于 JVM 实现)
这说明:SecurityManager 不是“让 exit 更安全”,而是把“是否能 exit”变成一个可审计、可配置的决策点。
现代 Java 中的替代性安全控制
由于 SecurityManager 已废弃,实际项目中更依赖以下方式实现等效管控:
- 字节码插桩:用 ByteBuddy 或 ASM 在类加载期重写 System.exit 调用,替换为日志记录 + 自定义钩子或抛出受检异常
- 容器/框架拦截:Spring Boot 可通过 ApplicationRunner 拦截启动异常,统一走 SpringApplication.exit(),避免裸调 exit
- CI/CD 门禁:静态扫描工具(如 SpotBugs、SonarQube)标记所有 System.exit 使用点,强制要求注释说明原因及状态码语义
- 运行时监控:通过 JVM TI 或 JFR 事件监听 Shutdown.start 事件,结合堆栈采样定位非法调用来源
这些做法不依赖 SecurityManager,却能达到更精细、更可观测的退出管控效果。

















