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