JVM 可以通过多种方式退出,通常可以归为以下几种类型:
### 1. 正常退出
- `System.exit(int status)`:
- 这是最常见的退出方式之一。你可以通过调用 System.exit()
方法来显式退出 JVM。这个方法会停止所有正在运行的线程,执行所有的 ShutdownHook
,并执行任何已注册的 finalizer
(如果有的话)。其中 status
参数是一个整数,通常 0
表示正常退出,非 0
表示异常退出。
- 主方法的正常返回:
- 当 main
方法执行完毕并返回时,JVM 会正常退出。这种情况下,JVM 会先执行所有 finally
块、`ShutdownHook`,然后完全退出。
- 线程结束:
- 如果 JVM 中的所有非守护线程都结束了执行,JVM 会自动退出。这是一种隐式退出方式。
### 2. 异常退出
- 未捕获的异常:
- 如果在主线程或非守护线程中抛出未捕获的异常,且该异常没有被处理,JVM 会崩溃并异常退出。这种情况会打印异常堆栈跟踪,并且不会执行 ShutdownHook
或 finally
块。
- `Runtime.getRuntime().halt(int status)`:
- 这个方法强制终止 JVM,并且不会执行任何 ShutdownHook
或 finalizer
。`halt()` 方法通常用于紧急情况下终止程序,并立即退出 JVM。
- 内部错误:
- JVM 内部的致命错误,例如 OutOfMemoryError
、`StackOverflowError` 或其他 JVM 内部错误,可能导致 JVM 异常退出。这种情况可能会在错误日志中看到相关的错误信息。
### 3. 强制退出
- 操作系统信号(`SIGKILL`、`SIGTERM` 等):
- 当 JVM 收到 SIGKILL
(如通过 kill -9
命令发送)信号时,会被立即终止。此时,JVM 没有机会执行任何清理操作,例如 finally
块、`ShutdownHook`。`SIGTERM` 是一个请求进程终止的信号,但它允许 JVM 优雅地退出,即执行 ShutdownHook
和 finally
块。SIGINT 信号(Ctrl + C触发)SIGINT 是一种可以被捕获的信号,这意味着程序可以编写代码来处理这个信号,例如进行清理工作或执行特定的操作,而不是立即退出。
1. SIGINT(通常由Ctrl + C触发):
• 默认行为是中断程序。如果你没有捕获该信号并自行处理,它可能会终止进程而不执行finally块。不过,若JVM在接收到SIGINT时有机会正常处理关闭操作,并且当前正在运行的线程能正常结束,那么finally块可能会被执行。
2. SIGTERM(kill命令的默认信号):
• 这个信号请求进程优雅地退出。JVM通常会尝试进行一个有序的关闭,如果程序中有finally块,且线程能正常结束,那么finally块很有可能会被执行。
3. SIGKILL(kill -9触发):
• 这个信号会立即强制终止进程,JVM无法捕获或处理这个信号。因此,在这种情况下,finally块不会被执行,因为进程会立刻被杀死,没有机会执行任何清理操作。
- 硬件故障:
- 硬件故障,如断电、内存故障或其他硬件相关问题,可能导致 JVM 突然终止。此类退出通常是无法控制的,也不会执行任何清理操作。
### 4. 崩溃
- JVM 崩溃:
- JVM 本身的崩溃(如由于JVM Bug、底层本地代码导致的崩溃、Java Native Interface (JNI) 错误等)会导致 JVM 立即退出。这通常会生成一个崩溃日志文件(例如 hs_err_pid.log
),详细描述崩溃原因。
### 总结:
- 正常退出:`System.exit()`、主方法结束、所有非守护线程结束。
- 异常退出:未捕获的异常、`Runtime.getRuntime().halt()`、JVM 内部错误。
- 强制退出:操作系统信号(`SIGKILL`)、硬件故障。
- 崩溃:JVM 崩溃、底层代码错误。