解读JVM的生命周期是怎么样的
作者:冰糖心书房
JVM的生命周期包括启动、运行和终止三个阶段,启动阶段包括创建JVM实例、加载和初始化核心类库、加载main方法所在的类和初始化类,运行阶段包括执行main方法、类加载、字节码执行、内存管理、线程管理和异常处理,终止阶段包括正常终止、异常终止和外部终止
JVM的生命周期是怎么样的
JVM 的生命周期是指 JVM 实例从启动到终止的整个过程。
一个 Java 应用程序通常对应一个 JVM 实例(除非使用 JNI 等技术在同一进程中创建多个 JVM 实例,但这并不常见)。
JVM 的生命周期阶段
启动 (Startup)
- 创建 JVM 实例: 当你使用
java
命令运行 Java 程序时,操作系统会创建一个新的进程,并在该进程中启动一个 JVM 实例。 - 加载和初始化:
- 查找并加载 JDK: 找到 JRE (Java Runtime Environment) 的位置.
- 创建引导类加载器 (Bootstrap Class Loader): 加载核心类库(
rt.jar
等)。 - 创建扩展类加载器 (Extension Class Loader) 和应用程序类加载器 (Application Class Loader): 分别加载扩展类库和应用程序的类。
- 加载
main
方法所在的类: 使用应用程序类加载器加载包含main
方法的类。 - 初始化类: 执行类的静态初始化块和静态变量赋值。
- 命令行参数: 解析传入
java
命令的参数.
运行 (Execution)
- 执行
main
方法: JVM 调用main
方法,开始执行 Java 程序。 - 类加载: 在程序运行过程中,根据需要动态加载类。
- 字节码执行: JVM 的执行引擎解释或编译执行字节码。
- 内存管理: JVM 分配和管理内存,进行垃圾回收。
- 线程管理: JVM 创建和管理线程。
- 异常处理: JVM 处理程序中发生的异常。
终止 (Termination)
正常终止:
- 当程序执行完
main
方法,并且所有非守护线程(non-daemon threads)都已结束时,JVM 正常终止。 - 可以通过调用
System.exit(status)
方法显式终止 JVM。status
是退出状态码(0 表示正常退出,非 0 表示异常退出)。
异常终止:
- 如果程序中发生了未捕获的异常,并且没有设置默认的未捕获异常处理器,JVM 会异常终止。
- 可以通过
Thread.setDefaultUncaughtExceptionHandler()
设置默认的未捕获异常处理器。
外部终止:
- 用户可以通过操作系统命令(例如,在 Linux 中使用
kill
命令)强制终止 JVM 进程。 - JVM 可能会收到操作系统发送的终止信号(例如,SIGTERM)。
钩子 (Shutdown Hooks):
- 在 JVM 终止之前,可以注册一些钩子函数(shutdown hooks),用于执行一些清理操作(例如,关闭数据库连接、释放资源等)。
- 可以使用
Runtime.getRuntime().addShutdownHook(Thread hook)
方法注册钩子函数。 - 钩子函数会在以下情况下执行:
- 程序正常退出。
- 调用
System.exit()
。 - 用户中断程序(例如,按下 Ctrl+C)。
- 系统关闭。
- 钩子函数执行的顺序是不确定的。
总结流程图
+----------------+ | 启动 JVM | (java 命令) +----------------+ | V +----------------+ | 加载和初始化 | | - 查找并加载 JDK | | - 创建类加载器 | | - 加载 main 类 | | - 初始化类 | +----------------+ | V +----------------+ | 运行程序 | | - 执行 main 方法| | - 类加载 | | - 字节码执行 | | - 内存管理 | | - 线程管理 | | - 异常处理 | +----------------+ | V +----------------+ | 终止 JVM | | - 正常终止 | | - 异常终止 | | - 外部终止 | | - 执行钩子函数 | +----------------+
代码示例 (演示钩子函数)
public class JVMLifecycle { public static void main(String[] args) { // 注册钩子函数 Runtime.getRuntime().addShutdownHook(new Thread(() -> { System.out.println("JVM 即将终止,执行清理操作..."); // 在这里执行清理操作 (例如,关闭数据库连接、释放资源等) })); System.out.println("程序开始执行..."); // 模拟程序运行一段时间 try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("程序执行完毕..."); // 可以选择显式退出 (也可以不调用 exit,让程序自然结束) // System.exit(0); } }
运行结果:
程序开始执行...
程序执行完毕...
JVM 即将终止,执行清理操作...
注意:
- 如果程序中存在死循环或无限等待,JVM 可能永远不会终止。
- 守护线程 (daemon thread) 不会阻止 JVM 终止。当所有非守护线程都结束后,JVM 会强制终止所有守护线程。
- 钩子函数应该尽量简短,避免执行耗时的操作,否则可能会导致 JVM 无法正常终止。
- 不要在钩子函数中调用
System.exit()
,否则会导致死循环。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。