bigywater.tech
Java · 397

JVM 进程退出钩子与退出状态

进程退出码是调度器、脚本和 CI 判断“这次运行成功了吗”的唯一依据。这篇量出 JVM 在各种退出路径上给出的退出码,哪些异常不会改变它,以及 OOM 相关选项如何改变进程的命运。

300 → 44
System.exit(300) 在 shell 里看到的退出码(对 256 取模)
0
子线程抛出未捕获异常、main 正常结束时的退出码(只有 main 抛出才是 1)
3
-XX:+ExitOnOutOfMemoryError 触发时的退出码(默认不加这个选项时子线程 OOM 后进程以 0 退出)

退出码的取值与异常线程

实测 · 本机执行输出
  System.exit(0) → shell 看到的退出码 0
  System.exit(1) → shell 看到的退出码 1
  System.exit(3) → shell 看到的退出码 3
  System.exit(255) → shell 看到的退出码 255
  System.exit(256) → shell 看到的退出码 0
  System.exit(257) → shell 看到的退出码 1
  System.exit(300) → shell 看到的退出码 44
  System.exit(-1) → shell 看到的退出码 255
  System.exit(-2) → shell 看到的退出码 254
  main 抛出未捕获异常 → 退出码 1;输出:Exception in thread "main" java.lang.IllegalStateException: main 抛出
  子线程抛出未捕获异常,main join 后正常结束 → 退出码 0;输出:main 正常结束 | Exception in thread "Thread-0" java.lang.IllegalStateException: 子线程抛出
  main 返回,稍后(200 ms)非 daemon 子线程抛出未捕获异常 → 退出码 0;输出:main 返回 | Exception in thread "Thread-0" java.lang.RuntimeException: 子线程稍后抛出
  main 抛出异常,但另一个非 daemon 线程还要跑 800 ms → 退出码 1;输出:子线程(非 daemon)正常结束 | Exception in thread "main" java.lang.IllegalStateException: main 抛出

多线程同时 exit

实测 · 本机执行输出
  30 次的退出码分布:{1: 18, 2: 12}(每次只有一个调用生效,另一个调用被阻塞;哪个先到由线程调度决定)

OutOfMemoryError

实测 · 本机执行输出
  [默认:main 里不捕获] 退出码 1;输出:Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHand
  [默认:main 捕获后继续] 退出码 0;输出:main 捕获了 OutOfMemoryError,继续运行并正常结束
  [默认:子线程 OOM 后 main 继续] 退出码 0;输出:main 在子线程 OOM 之后继续:join 返回 | Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHand
  [-XX:+ExitOnOutOfMemoryError] 退出码 3;输出:Terminating due to java.lang.OutOfMemoryError: Java heap space
  [-XX:+ExitOnOutOfMemoryError,子线程 OOM] 退出码 3;输出:Terminating due to java.lang.OutOfMemoryError: Java heap space
  [-XX:+CrashOnOutOfMemoryError] 退出码 1;输出:Aborting due to java.lang.OutOfMemoryError: Java heap space | # | # A fatal error has been detected by the Java Runtime Environment: | #;生成了 hs_err_oom.log(68 KiB)
  [-XX:OnOutOfMemoryError="echo ...;"] 退出码 1;输出:# | # java.lang.OutOfMemoryError: Java heap space | # -XX:OnOutOfMemoryError="echo 外部命令被执行了" | # Executing "echo 外部命令被执行了"...

启动器错误

实测 · 本机执行输出
  主类不存在 → 退出码 1;输出首行:Error: Could not find or load main class NoSuchMain
  主类存在但没有 main 方法 → 退出码 1;输出首行:Error: Main method not found in class NoMain, please define the main method as:
  无效的堆大小 -Xmx0m → 退出码 1;输出首行:Invalid maximum heap size: -Xmx0m
  未知的 JVM 选项 → 退出码 1;输出首行:Unrecognized VM option 'NoSuchFlag'
  -version → 退出码 0;输出首行:openjdk version "21.0.12.1" 2026-08-18
  栈溢出(main 无限递归) → 退出码 1;输出首行:Exception in thread "main" java.lang.StackOverflowError

分步演示

JVM 进程退出状态
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12;退出码由 Python subprocess 读取(信号终止为负值;本次实验里没有信号终止);OOM 实验用 -Xmx16m
    • 没有验证:Windows 的退出码范围、容器里 PID 1 的退出码传播、-XX:+ExitOnOutOfMemoryError 在 JDK 17 以前版本的行为、JVM 真正崩溃(SIGSEGV)时的退出码与 core 文件(CrashOnOutOfMemoryError 关闭了 core dump,所以这里观察到的是 1,不是 SIGABRT 的 134)
    京ICP备2026067502号-1京公网安备11010502063487号