JVM 的生命周期对应一个独立运行的 Java 程序(进程级别),是 JVM 实例从启动→运行→终止的完整过程。
一、启动阶段:JVM 实例的初始化(创建→就绪)
当你执行java 类名命令时,操作系统会创建新进程并初始化 JVM 实例,核心是加载 JVM 运行所需的核心组件和程序入口类。
| 步骤 | 核心操作 | 通俗解释 & 关键说明 |
|---|---|---|
| 1. 创建 JVM 实例 | 操作系统调用java.exe(Windows)/java(Linux),分配进程内存,初始化类装载子系统、执行引擎、运行时数据区 |
JVM 先 "搭好架子",为后续执行程序做准备 |
| 2. 加载核心类库 | 引导类加载器(C++ 实现)加载$JAVA_HOME/jre/lib/rt.jar等核心类(如Object、String) |
这是 JVM 的 "基础工具包",必须最先加载 |
| 3. 创建类加载器链 | 依次创建扩展类加载器(加载jre/lib/ext)、应用类加载器(加载 classpath 下的用户类) |
形成「引导→扩展→应用」的类加载器层级,为后续加载用户类做准备 |
| 4. 加载并初始化 main 类 | 应用类加载器加载包含main方法的类,执行静态代码块、静态变量赋值(父类优先初始化) |
main类是程序入口,初始化完成后才具备执行条件 |
| 5. 解析命令行参数 | 解析-Xmx512m(堆内存)、-Dkey=value(系统属性)等参数,应用到 JVM 配置 |
定制 JVM 运行环境,比如调整内存大小 |
核心特点
- 引导类加载器不继承
java.lang.ClassLoader,是类加载的 "根"; - 初始化顺序:父类静态代码 / 变量 → 子类静态代码 / 变量。
二、运行阶段:程序执行与资源管理(核心阶段)
启动完成后,JVM 进入最长的运行阶段,核心是执行main方法并管理内存、线程、异常等资源。
1. 核心执行逻辑
-
执行入口:调用
main方法(非守护线程),触发程序逻辑执行; -
类加载规则:运行时动态加载未加载的类,遵循双亲委派模型(子类加载器先委托父类加载,父类加载失败才自己加载);
-
字节码执行方式:
- 解释执行:逐行解释字节码(启动快、效率低);
- JIT 编译:对热点代码(频繁执行的方法 / 循环)编译为机器码(启动慢、执行快)。
2. 核心资源管理
(1)内存管理(运行时数据区)
JVM 通过以下区域管理内存,也是 GC(垃圾回收)的核心操作对象:
| 区域 | 作用 | 核心特点 |
|---|---|---|
| 堆(Heap) | 存储对象实例(如new Object()) |
GC 主要区域,分为新生代(Eden/Survivor)、老年代 |
| 方法区(元空间) | 存储类元数据、静态变量、字符串常量池 | JDK8 + 用元空间(本地内存)替代永久代,避免永久代溢出 |
| Java 栈 | 线程私有,存储方法栈帧(局部变量、操作数栈) | 每个方法调用对应一个栈帧,方法结束则栈帧出栈 |
| 本地方法栈 | 为native方法(如System.out.println())提供栈空间 |
支撑 Java 调用 C/C++ 实现的底层方法 |
| 程序计数器 | 线程私有,存储当前执行的字节码行号 | 唯一不会抛出OutOfMemoryError的区域 |
(2)垃圾回收(GC)
- 触发条件:新生代满(Minor GC)、老年代满(Full GC)、手动调用
System.gc()(不推荐); - 常用回收器:Serial(单线程)、Parallel(吞吐量优先)、G1(分区、低停顿)、ZGC(亚毫秒级停顿)。
(3)线程管理
- 非守护线程(如
main线程):JVM 必须等所有非守护线程结束才会终止; - 守护线程(如 GC 线程):由 JVM 管理,不阻止 JVM 终止;
- 线程状态:新建→就绪→运行→阻塞→终止。
(4)异常处理
通过try-catch-finally、throw、throws处理异常,未捕获的异常会终止当前线程,但不影响其他线程。
三、终止阶段:JVM 实例的退出(清理→结束)
当满足终止条件时,JVM 会执行清理操作并退出进程,终止方式分为以下 4 类:
| 终止方式 | 触发条件 | 执行流程 | 注意事项 |
|---|---|---|---|
| 正常终止 | 所有非守护线程执行完毕 | JVM 自动清理资源(释放内存、关闭文件句柄)→ 退出进程 | 最理想的终止方式 |
| 显式终止 | 调用System.exit(int status)/Runtime.getRuntime().exit() |
先执行关闭钩子 → 终止所有线程 → 释放资源 → 退出 | status=0表示正常退出,非 0 为异常退出 |
| 异常终止 | 未捕获的严重异常(如OutOfMemoryError) |
终止异常线程 → 若所有非守护线程结束,则 JVM 退出 | 可能导致资源未释放 |
| 外部终止 | 操作系统命令(kill -9 PID/taskkill /F /PID) |
直接终止进程,不执行任何清理操作 | 可能引发资源泄漏(如未关闭的数据库连接) |
关键机制:关闭钩子(Shutdown Hook)
关闭钩子是 JVM 终止前执行的清理逻辑(如关闭数据库连接、记录日志),仅在正常 / 显式终止时执行(外部终止不执行)。
代码示例:注册关闭钩子
java
package com.dwl.ex01_类加载子系统;
/**
* @ClassName JVMShutdownHookDemo
* @Description JVM关闭钩子(ShutdownHook)演示示例
* 关闭钩子是JVM在退出前自动执行的线程,用于优雅地清理资源(如关闭数据库连接、释放文件句柄、保存程序状态等)
* @Version 1.0.0
* @Date 2026/1/18
* @Author By Dwl
*/
public class JVMShutdownHookDemo {
/**
* 程序入口方法
* @param args 命令行参数(本示例未使用)
*/
public static void main(String[] args) {
// 1. 注册JVM关闭钩子
// Runtime.getRuntime():获取当前应用程序的运行时环境对象,用于与JVM交互
// addShutdownHook(Thread hook):向JVM注册一个关闭钩子线程
// 钩子线程会在JVM即将退出时被启动执行(JVM不保证多个钩子的执行顺序)
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
// 钩子线程的核心逻辑:执行资源清理操作
System.out.println("===== 执行关闭钩子:开始清理资源 =====");
// 实际业务场景中,可在这里执行以下操作:
// 1. 关闭数据库连接池(如JDBC Connection、Redis连接等)
// closeDBConnection();
// 2. 释放打开的文件句柄、网络套接字
// releaseFileHandles();
// 3. 保存程序运行状态(如内存中的临时数据写入文件)
// saveProgramState();
// 4. 记录程序退出日志
// logExitInfo();
System.out.println("===== 关闭钩子执行完成:资源清理完毕 =====");
}, "Custom-Shutdown-Hook-Thread")); // 给钩子线程命名,便于排查问题
// 2. 模拟程序正常运行逻辑
System.out.println("程序正常运行中...(等待5秒,可按Ctrl+C或等待自动退出触发钩子)");
// 模拟程序执行耗时操作(睡眠5秒)
try {
// 线程睡眠5秒,模拟业务逻辑执行
Thread.sleep(5000);
System.out.println("程序正常执行完毕,即将触发JVM退出...");
} catch (InterruptedException e) {
// 捕获线程中断异常(如其他线程中断当前主线程)
System.out.println("主线程睡眠被中断:" + e.getMessage());
e.printStackTrace();
}
// 3. 显式终止JVM(可选)
// System.exit(0):主动退出JVM,会触发关闭钩子执行
// 注:如果注释掉这行,程序执行完main方法后正常退出,也会触发钩子;按Ctrl+C中断程序同样会触发
// System.exit(0);
}
// ---------------------- 以下为示例业务方法(注释掉仅作演示) ----------------------
/**
* 示例:关闭数据库连接
*/
// private static void closeDBConnection() {
// System.out.println("关闭数据库连接池...");
// // 实际代码:关闭Connection、释放连接池资源等
// }
/**
* 示例:保存程序运行状态
*/
// private static void saveProgramState() {
// System.out.println("保存程序临时状态到文件...");
// // 实际代码:将内存中的数据写入本地文件/数据库
// }
}
关闭钩子注意事项
- 逻辑要简短,避免耗时操作(否则可能导致 JVM 无法正常终止);
- 不要调用
System.exit()(会引发死循环); - 多个钩子的执行顺序不确定。
四、JVM 生命周期流程图
正常终止(非守护线程结束)
显式终止(System.exit)
异常终止(未捕获异常)
外部终止(kill -9)
执行java命令
启动阶段:创建JVM实例→加载核心类库→初始化main类
运行阶段:执行main方法→动态加载类→内存/线程/异常管理
终止条件?
执行关闭钩子→清理资源→JVM退出
终止异常线程→若所有非守护线程结束→JVM退出
直接终止进程→无清理操作
五、核心特性与实际应用建议
1. 核心特性
- 进程隔离:每个 Java 程序对应一个独立的 JVM 实例,实例间相互隔离;
- 不可逆性:JVM 实例终止后,无法重启,需重新执行
java命令创建新实例; - 守护线程不阻终止:仅非守护线程会决定 JVM 的生命周期。
2. 实际应用建议
- 避免内存泄漏:及时移除静态集合中的无用对象,防止对象被长期持有;
- 优化 GC 性能:根据业务场景调整堆大小(
-Xms/-Xmx)、选择合适的 GC(如高并发场景用 G1/ZGC); - 合理使用关闭钩子:仅执行必要的清理操作,避免复杂逻辑;
- 处理未捕获异常:通过
Thread.setDefaultUncaughtExceptionHandler()设置全局异常处理器,避免程序崩溃。
总结
- JVM 生命周期分为启动(初始化核心组件)、运行(执行程序 + 管理资源)、终止(清理 + 退出)三个核心阶段;
- 双亲委派模型、GC、关闭钩子是各阶段的核心机制,直接影响程序稳定性和性能;
- 外部终止(如
kill -9)会跳过清理流程,实际开发中应优先通过正常 / 显式方式终止 JVM
JVM(Java Virtual Machine)的生命周期 核心考点记忆口诀
启创环境加载类,运行执码管线程;满足条件即消亡,资源释放无残留。
-
逐句拆解:
- 启创环境加载类:JVM 启动时先初始化内存分区、类加载子系统等核心环境,再加载入口类(如含 main 方法的类)和核心类库(rt.jar);
- 运行执码管线程:运行阶段核心是执行字节码指令,同时管理用户线程 / 守护线程(线程状态是退出的关键),还包含类的链接 / 初始化、GC 等操作;
- 满足条件即消亡:触发退出条件(如下文)时,JVM 进入终止流程;
- 资源释放无残留:JVM 销毁时会彻底释放内存、CPU 等系统资源,无残留占用。
JVM 消亡的核心触发条件:
- 所有非守护线程执行完毕(最常见);
- 主动调用
System.exit(int)方法; - 虚拟机被外部终止(如 kill 命令、任务管理器结束);
- 严重异常 / 错误导致虚拟机崩溃(如 OOM 未处理)。