Java 虚拟机 (JVM) 的启动是一个复杂的过程,涉及多个阶段的初始化和配置。

目录

启动方式

启动流程

启动器阶段(Launcher Phase)

启动器工作

注意事项

JVM 初始化阶段

JVM 初始化的核心步骤

应用层

常见问题与验证方法

启动方式

JVM 启动通常通过以下方式:

命令行执行 java 命令IDE 中运行 Java 程序其他 Java 应用服务器或容器启动

启动流程

JVM 启动流程的核心是 分层协作:

启动器层(java 命令)处理用户输入。JVM 层(动态库)管理内存、线程、字节码执行。应用层运行 Java 代码,依赖 JVM 的运行时支持。

启动器阶段(Launcher Phase)

启动器阶段是 JVM 启动流程的第一步,由 JDK 提供的 java(或 java.exe)可执行文件负责完成。它的核心任务是 准备 JVM 运行环境,并最终加载 JVM 核心库(如 HotSpot 的 jvm.dll/libjvm.so)。

启动器工作

(1) 解析命令行参数

java -Xms256m -Xmx1024m -Dapp.name=Demo MainClass arg1 arg2

参数分类:

类型示例作用标准选项-version, -classpath所有 JVM 通用的基础参数-X 非标准选项-Xss256k, -Xloggc:file特定 JVM 实现的扩展参数-XX 高级选项-XX:+UseG1GCJVM 调优/调试参数系统属性-Dkey=value传递给 Java 程序的键值对主类与参数MainClass arg1 arg2程序入口类及其命令行参数

(2) 确定 JVM 动态库路径

通过 JAVA_HOME 环境变量定位 JDK 安装目录。加载 JVM 实现库(如 HotSpot 的 jvm.dll/libjvm.so):

# Linux/macOS 默认路径

$JAVA_HOME/lib/server/libjvm.so

# Windows 默认路径

%JAVA_HOME%\bin\server\jvm.dll

(3) 计算初始内存分配

堆内存:若未指定 -Xms/-Xmx,按物理内存比例计算默认值:

初始堆(-Xms)= 物理内存 / 64(但至少 8MB)最大堆(-Xmx)= 物理内存 / 4(但不超过 1GB 默认值)

线程栈:未指定 -Xss 时,默认 1MB(Linux x64)或 320KB(Windows x86)。

(4) 设置执行模式

客户端模式(-client):快速启动,适合 GUI 应用(32 位系统默认)。服务器模式(-server):激进优化,适合后端服务(64 位系统默认)。

注意事项

启动器工作步骤不是严格线性执行:某些步骤会交叉进行,如内存分配可能在库加载期间就开始

设置了-Xms不等于立即占用:现代OS使用惰性内存分配策略

版本差异:

JDK 8与JDK 17+的启动流程有显著不同模块系统(JDK9+)增加了模块解析阶段

平台特异性:

Windows和Linux的库加载机制不同macOS有额外的安全检查

JVM 初始化阶段

JVM 初始化阶段是启动器(java 命令)加载 jvm.dll/libjvm.so 后,调用 JNI_CreateJavaVM() 函数触发的核心过程。这一阶段会完成 内存分配、子系统初始化、类加载体系构建 等关键操作,为执行 Java 程序做好准备。

JVM 初始化的核心步骤

(1) 调用 JNI_CreateJavaVM() 入口函数:JVM 动态库(如 HotSpot 的 jvm.dll)暴露的 JNI 接口。

主要任务:

创建 JavaVM 和 JNIEnv 对象(JNI 交互的核心句柄)。初始化 JVM 的全局状态和子系统。

(2) 初始化内存 线程私有区域(随线程创建分配):

程序计数器(PC Register):记录当前线程执行的指令地址。Java 虚拟机栈(JVM Stack):存储栈帧(局部变量表、操作数栈等)。本地方法栈(Native Method Stack):服务于 JNI 方法。

共享区域(JVM 进程全局):

堆内存(Heap):

根据 -Xms(初始堆)和 -Xmx(最大堆)分配空间。

划分 Young Gen(Eden/S0/S1)和 Old Gen(G1/ZGC 等可能有不同布局)。

方法区(Metaspace):

取代 JDK8 之前的 PermGen,存储类元数据、常量池等。

大小由 -XX:MetaspaceSize 和 -XX:MaxMetaspaceSize 控制。

代码缓存(Code Cache):

存储 JIT 编译后的本地代码,由 -XX:ReservedCodeCacheSize 配置。

(3) 核心子系统初始化

子系统初始化内容关键参数类加载系统构建 BootstrapClassLoader、AppClassLoader 等层级-Xbootclasspath执行引擎解释器、JIT 编译器(C1/C2)、字节码验证器-XX:+TieredCompilation垃圾回收器初始化 GC 线程和内存管理策略(如 G1、ZGC)-XX:+UseG1GC线程管理系统创建主线程(main)和 GC 线程-XX:ParallelGCThreads安全子系统加载安全策略(如 java.policy)-Djava.security.manager

(4) 类加载器体系构建

1.Bootstrap ClassLoader:

加载 jre/lib/rt.jar 等核心库(由 C++ 实现,无 Java 对象)。

2.Platform ClassLoader(JDK9+):

取代 Extension ClassLoader,加载 jre/lib/ext 等模块。

3.Application ClassLoader:

加载用户类路径(-classpath 或 CLASSPATH 环境变量)。

应用层

Java 应用层的所有能力(如创建对象、启动线程、捕获异常)都通过 JVM 的运行时服务实现。

常见问题与验证方法

启动器阶段 vs JVM 初始化阶段

对比项启动器阶段JVM 初始化阶段执行主体java 可执行文件jvm.dll/libjvm.so 动态库核心任务参数解析、JVM 库加载内存分配、子系统初始化关键函数main()(C++)JNI_CreateJavaVM()错误表现"Error: could not create JVM""OutOfMemoryError: Metaspace"

1.如何确认启动器加载的 JVM 路径?

# Linux/macOS

java -verbose:jni 2>&1 | grep "JNI_CreateJavaVM"

# Windows(需使用工具如 Process Explorer 查看 DLL 加载)

2.启动器阶段可能失败的原因?

错误原因:

JAVA_HOME 配置错误。缺少 jvm.dll/libjvm.so(如仅安装 JRE 而非 JDK)。操作系统位数不匹配(如尝试用 32 位 java 加载 64 位 JVM)。

# 检查 JVM 库是否存在

ls $JAVA_HOME/lib/server/libjvm.so

# 验证系统位数

java -version # 输出包含 "64-Bit" 则为 64 位

3.OutOfMemoryError 在何时抛出?

堆内存不足:对象分配失败时(如 -Xmx 设置过小)。Metaspace 不足:类加载过多(如动态生成类)。栈溢出:递归过深(-Xss 设置过小)。

4.如何验证内存初始化?

# 查看堆内存参数

java -XX:+PrintFlagsFinal -version | grep HeapSize

# 跟踪类加载和内存分配

java -XX:+TraceClassLoading -XX:+PrintTLAB MainClass