JVM 生命周期:从启动到退出的完整历程
JVM(Java 虚拟机)作为运行字节码的虚拟进程,其生命周期遵循 “启动 - 执行 - 退出” 的完整流程。理解这一过程有助于深入掌握 JVM 的运行机制,以及排查与进程生命周期相关的问题(如进程异常退出、资源未释放等)。
JVM 启动:初始化与类加载
JVM 的启动是一个从无到有创建虚拟机进程的过程,核心是通过引导类加载器加载初始类,完成虚拟机的初始化。
1. 启动触发方式
- 命令行启动:通过
java命令启动(如java -jar app.jar或java com.example.Main),底层调用 JVM 的启动接口(如JNI_CreateJavaVM)。 - 嵌入式启动:通过 JNI(Java Native Interface)在其他语言(如 C/C++)中嵌入 JVM,手动调用启动接口创建虚拟机实例。
2. 启动核心步骤
- 创建 JVM 实例:操作系统分配进程资源,初始化 JVM 的内部数据结构(如内存管理模块、线程管理模块)。
- 初始化引导类加载器:引导类加载器(Bootstrap ClassLoader)是 JVM 自带的加载器,负责加载 JVM 运行必需的核心类(如
java.lang.Object、java.lang.ClassLoader等,位于rt.jar中)。 - 加载初始类:
初始类由虚拟机实现指定(通常是用户程序的入口类,即包含main方法的类)。引导类加载器通过类的全限定名找到并加载该类,触发类加载的 “加载 - 链接 - 初始化” 流程(见前文)。 - 启动主线程:初始化完成后,JVM 创建主线程(
main线程),执行初始类的main方法,标志着程序开始执行。
3. 启动阶段的关键特点
- 启动过程依赖 JVM 的配置参数(如堆大小
-Xms、垃圾回收器-XX:+UseG1GC等),这些参数会影响内存分配、执行引擎等核心组件的初始化。 - 若初始类加载失败(如类不存在、字节码损坏),JVM 会在启动阶段抛出错误并终止。
JVM 执行:运行 Java 程序
JVM 启动后进入执行阶段,其核心任务是运行 Java 程序(字节码),直到程序结束或异常终止。
1. 执行阶段的核心行为
- 线程管理:JVM 会创建和管理多个线程(如主线程、GC 线程、守护线程等),线程的生命周期与 JVM 进程绑定。
- 内存管理:
- 动态分配对象内存(主要在堆中);
- 垃圾回收器定期回收不可达对象的内存,避免内存泄漏。
- 字节码执行:通过解释器和 JIT 编译器协同执行字节码,热点代码会被编译为机器码以提升效率。
- 异常处理:当程序抛出未捕获的异常时,JVM 会终止对应的线程;若主线程异常终止,可能导致 JVM 退出。
2. 执行阶段的生命周期特征
- JVM 进程的生命周期与程序的生命周期一致:程序运行时 JVM 持续运行,程序结束后 JVM 退出。
- 执行过程中,JVM 通过 JMX(Java Management Extensions)等接口提供运行时监控能力(如查看内存使用、线程状态等)。
JVM 退出:终止进程与释放资源
JVM 退出意味着虚拟机进程终止,所有资源(内存、文件句柄、线程等)会被释放。JVM 退出的触发方式可分为正常退出和异常退出两类。
1. 正常退出
程序正常执行完毕,JVM 主动终止进程,常见场景:
- 程序执行完成:
main方法执行结束,主线程退出,且没有其他非守护线程运行(JVM 会等待所有非守护线程结束后退出)。 - 主动调用退出方法:
- 调用
System.exit(int status)或Runtime.getRuntime().exit(int status):终止 JVM,status为退出状态码(0 表示正常,非 0 表示异常)。 - 调用
Runtime.getRuntime().halt(int status):强制终止 JVM,不执行退出钩子(shutdown hook),直接终止进程。
- 调用
2. 异常退出
因错误或外部干预导致 JVM 被迫终止,常见场景:
- 未捕获的异常 / 错误:线程抛出未捕获的
Error(如OutOfMemoryError)或RuntimeException,导致线程终止;若所有非守护线程终止,JVM 退出。 - 操作系统干预:
- 外部进程发送终止信号(如
kill -9 <pid>在 Linux 中强制杀死 JVM 进程)。 - 操作系统崩溃或断电,导致 JVM 进程异常终止。
- 外部进程发送终止信号(如
- JNI 调用错误:通过 JNI 调用的本地方法执行错误(如访问非法内存),可能导致 JVM 崩溃。
3. 退出前的清理动作
JVM 正常退出前会执行一系列清理操作:
- 执行退出钩子(Shutdown Hooks):通过
Runtime.getRuntime().addShutdownHook(Thread hook)注册的线程会在 JVM 退出前执行,用于释放资源(如关闭文件、断开数据库连接等)。 - Finalizer 机制:部分对象的
finalize()方法可能被执行(但不保证一定执行,不建议依赖)。 - 释放资源:JVM 释放分配的内存、关闭文件句柄、终止所有线程等,最终操作系统回收进程资源。
v1.3.10