深入探索剖析 JVM 的启动过程

news/2025/11/16 21:20:51/文章来源:https://www.cnblogs.com/didispace/p/19229084

你可曾想过:当你在终端里敲下 java,在 main 方法真正运行之前,JVM 为了“创造一个可运行你的程序的宇宙”,到底经历了哪些步骤?从参数校验、系统资源探测,到选择垃圾回收器,再到类的加载、链接与初始化,这些看不见的过程决定了应用的启动体验与后续性能。本文用一个极简的 HelloWorld 贯穿全程,结合详细日志,一步步洞察 JVM 的启动机制,帮你在调试和性能优化时更有抓手。

1. 概览

当我们运行一个 Java 应用时,JVM 在我们的代码真正开始执行之前,会先完成一系列复杂步骤。本文将从执行 java 命令的那一刻开始,一直走到应用就绪。

我们以一个简单的 HelloWorld 程序为例,拆解每一个阶段。理解这些内部机制能显著提升调试与性能调优的效果。

2. 从 java 命令到 JVM 启动

在 JVM 执行任何代码之前,它需要先启动、校验输入并配置运行环境。下面按启动顺序走一遍早期流程:从调用 java 命令到初始化 JVM 运行时。

2.1. java 命令与初始调用

当我们运行 java 命令时,JVM 启动序列会通过 JNI 方法 JNI_CreateJavaVM() 开始执行。该方法完成若干关键初始化任务,为执行 Java 应用准备环境。Java Native Interface(JNI)是 JVM 与原生系统库之间的桥梁,使 Java 与平台特性可以双向通信。

本文将使用详细日志观察 JVM 的内部运作,例如:

java -Xlog:all=trace HelloWorldCopy

2.2. 校验用户输入

首先,JVM 会校验我们传入的参数:

[0.006s][info][arguments] VM Arguments:
[arguments] jvm_args: -Xlog:all=trace:file=helloworld.log 
[arguments] java_command: HelloWorld
[arguments] java_class_path (initial): .
[arguments] Launcher Type: SUN_STANDARD

JVM 会验证目标可执行、类路径以及任何 JVM 参数,确保它们在继续执行前都是有效的。这个步骤能尽早捕获很多常见配置错误,避免后续阶段出现更难定位的问题。

2.3. 检测系统资源

接着,JVM 会识别可用的系统资源,例如处理器数量、内存大小以及关键系统服务:

[0.007s][debug][os       ] Process is running in a job with 20 active processors.
[os       ] Initial active processor count set to 20
[os       ] Process is running in a job with 20 active processors.
[gc,heap  ]   Maximum heap size 4197875712
[gc,heap  ]   Initial heap size 262367232
[gc,heap  ]   Minimum heap size 6815736
[os       ] Host Windows OS automatically schedules threads across all processor groups.
[os       ] 20 logical processors found.

这些信息会影响 JVM 的一些内部决策,比如默认选择哪个垃圾回收器。可用 CPU 数和总内存会直接影响 JVM 的启发式选择。不过,大多数设置都可以通过显式的 JVM 参数进行覆盖。在这个阶段,JVM 还会检查是否支持 Native Memory Tracking,并验证它可能依赖的各类操作系统工具的可用性。

2.4. 环境准备

随后,JVM 会生成 HotSpot 性能数据。这些数据会被 JConsole、VisualVM 等工具用于检查和分析 JVM:

[perf,datacreation] name = sun.rt._sync_Inflations, dtype = 11, variability = 2, units = 4, dsize = 8, vlen = 0, pad_length = 4, size = 56, on_c_heap = FALSE, address = 0x000001f3085f0020, data address = 0x000001f3085f0050

这类性能数据通常存储在系统的 /tmp 目录下,并会在启动阶段的一段时间里持续生成,与其他初始化任务并行进行。

3. 加载、链接与初始化

当 JVM 环境就绪后,它会开始为我们的程序执行做准备。

3.1. 选择垃圾回收器

在 JVM 内部,一个关键步骤是选择垃圾回收器(GC)。截至 JDK 23,默认情况下 JVM 会选择 G1 GC,除非系统可用内存少于 1792MB 和/或仅有单处理器:

[gc               ] Using G1
[gc,heap,coops    ] Trying to allocate at address 0x0000000705c00000 heap of size 0xfa400000
[os               ] VirtualAlloc(0x0000000705c00000, 4198498304, 2000, 4) returned 0x0000000705c00000.
[os,map           ] Reserved [0x0000000705c00000 - 0x0000000800000000), (4198498304 bytes)
[gc,heap,coops    ] Heap address: 0x0000000705c00000, size: 4004 MB, Compressed Oops mode: Zero based, Oop shift amount: 3
[pagesize         ] Heap:  min=8M max=4004M base=0x0000000705c00000 size=4004M page_size=4K

当然,我们也可以选择其它 GC:如 Parallel GC、ZGC 等,具体可用与默认策略依不同 JDK 版本与发行版而异。

3.2. 加载 CDS(类数据共享)

此时,JVM 会开始寻找进一步的优化机会。CDS 是一组已经经过预处理的类文件归档,可以改善 JVM 的启动性能:

[cds] trying to map [Java home]/lib/server/classes.jsa
[cds] Opened archive [Java home]/lib/server/classes.jsa

不过,CDS 正在被 Project Leyden 中的 AOT(提前)机制逐步替代,后文会继续讨论。

3.3. 创建方法区

JVM 随后会创建“方法区”,这是一个用于存储类数据的特殊离堆内存区域。在 HotSpot 中,这一区域被称为 metaspace。当关联的类加载器不再可达时,存储于此的类数据也会被移除:

[metaspace,map    ] Trying anywhere...
[metaspace,map    ] Mapped at 0x000001f32b000000

虽然方法区不在堆中,但它仍由 GC 管理。

3.4. 类加载

类加载包含三个步骤:定位二进制表示、根据其派生出类、并将其加载到方法区。正是这种动态加载能力,让 Spring、Mockito 等框架可以在运行期按需生成并加载类。

类加载有两种方式:引导类加载器(bootstrap class loader)或自定义类加载器。下面借助一个简单的 HelloWorld 类,看看 JVM 首先会做什么:

public class HelloWorld extends Object {public static void main(String[] args) {System.out.println("Hello World!");}
}

JVM 会优先加载 java.lang.Object 及其依赖。类在初次加载时大多处于“半隐藏”的状态,以便进行必要的验证与整理工作。

再看下 java.lang.Object 的方法:

public class Object {public final native Class<?> getClass()public String toString()public boolean equals(Object obj)
}

这些方法分别引用了 java.lang.Classjava.lang.String,因此它们也需要先行加载。JVM 采用“按需加载”的策略,仅在类被实际引用时才加载。不过,上述这些对 JVM 至关重要的类会被“抢先加载”。在一个简单的 HelloWorld 程序里,由 JNI_CreateJavaVM() 初始化的引导类加载器负责所有的类加载工作。

3.5. 类链接

类链接可以拆分为验证(Verification)、准备(Preparation)与解析(Resolution),其发生顺序并不固定:解析可能发生在验证之前,也可能在类初始化之后。验证确保类结构正确:

[class,init] Start class verification for: HelloWorld
[verification] Verifying class HelloWorld with new format
[verification] Verifying method HelloWorld.<init>()V

位于 CDS 中的类已经过验证,因此会跳过该步骤,从而提升启动性能。这是 CDS 的重要收益之一。在“准备”阶段,JVM 会用默认值初始化静态字段,没有显式初始化器的静态变量会自动获得默认值。

在“解析”阶段,JVM 会解析常量池(Constant Pool)中的符号引用。常量池保存了类的所有符号引用,JVM 必须先将其解析为真实的内存引用,才能执行相关指令。

我们可以使用 javap 来观察:

javap -verbose HelloWorldCopy

这将显示常量池的内容:

Constant pool:#1 = Methodref          #2.#3          // java/lang/Object."<init>":()V#2 = Class              #4             // java/lang/Object#3 = NameAndType        #5:#6          // "<init>":()V#7 = Fieldref           #8.#9          // java/lang/System.out:Ljava/io/PrintStream;#13 = String             #14            // Hello World

构造器的字节码并不直接包含地址。它引用常量池中的符号项(例如 #1),这些条目描述了方法或字段。解析阶段会将这些符号项转为可执行的真实内存引用:

public HelloWorld();descriptor: ()Vflags: (0x0001) ACC_PUBLICCode:stack=1, locals=1, args_size=10: aload_01: invokespecial #1                  // Method java/lang/Object."<init>":()V4: returnLineNumberTable:line 2: 0line 4: 4

第 1 行的 invokespecial 指令引用了常量池条目 #1,其中包含链接到 java.lang.Object 构造器所需的信息。<init> 表示这是由 javac 为每个构造器自动生成的特殊方法。JVM 采用“延迟解析”,只有在尝试执行类中的某条指令时才触发解析;并非所有已加载的类都会实际执行其指令。

3.6. 类初始化

类初始化会为静态字段赋值并执行静态初始化器,这与我们调用构造器的实例初始化不同。该过程由 javac 自动生成的特殊方法 clinit 负责。

4. 优化 JVM 启动性能

尽管 JVM 的启动已经很高效,但仍有提升空间。以下是一些方向。

4.1. 类加载的影响

我们可以使用系统的 time 工具来度量 JVM 启动、加载类、链接并执行这个简单程序的总耗时:

time java HelloWorldCopy

该工具会测量从 JVM 进程启动到退出的挂钟时间,包含类加载、链接、JIT 预热与程序执行——不仅仅是用户代码。对于 HelloWorld,JVM 在启动期间通常会加载约 400~450 个类。在现代硬件上,即便开启冗长日志,整个过程也大约在 60 毫秒左右完成。

4.2. Project Leyden

Project Leyden 的目标是减少启动时间、达到峰值性能的时间以及内存占用。JDK 24 引入了 JEP 483:Ahead-of-Time Class Loading and Linking(提前类加载与链接),将这些操作从启动时前移至 AOT 阶段。

该特性会在“训练运行”中记录 JVM 的行为,将其存入缓存,并在后续启动时从缓存加载。这将取代原先的 CDS 概念,并最终以 AOT 的更广泛能力来统一表达。

4.3. JVM 参数与调优

虽然我们可以通过静态字段与初始化器在某些场景中优化启动性能,但应谨慎对待。为了将行为挪到类加载阶段而进行重构,往往很难获得可测量的收益——特别是考虑到运行时的大部分代码来自依赖库而非我们自己的应用。

5. 结论

本文从校验用户输入、检测系统资源,到类的加载、链接与初始化,系统地梳理了 JVM 在启动阶段经历的复杂流程。即便是一个简单的 HelloWorld,JVM 也会在执行代码之前构建起完整的运行环境,加载数百个类。

随着 Project Leyden 等改进(例如 AOT)的到来,JVM 的启动性能还将进一步提升。

更多相关技术干货分享可以关注这个Java专题

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/967365.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

noip8多校2

11.1511.15 t1 赛时想假2.75h,敲了10k(咋做到的?不知道) 曼哈顿距离转切比雪夫距离。 详见 直接出结论: 将每个坐标 \((x,y)\) 变作 \((x+y,x−y)\) 后,原坐标的曼哈顿距离等于新坐标的切比雪夫距离。 证明: 拆式…

2025年11月防冻液厂家推荐排行:五家实力对比与选购指南

立冬之后,北方夜间温度已逼近冰点,南方昼夜温差也迅速拉大,工业循环冷却、空气能热泵、数据中心冷却、冷链物流、冰雪场馆制冰等场景陆续进入“防冻关键期”。大量工程商、设备运维方、物业后勤和经销商开始集中备货…

2025年11月防冻液厂家推荐对比:五家资质与性能全维度排行

进入11月,华北、华东集中供暖启动,西北、东北工业循环冷却系统面临-20℃以下考验,防冻液采购高峰随之到来。用户普遍面临三大痛点:一是低温粘度升高导致泵耗上升,二是缓蚀体系不匹配造成换热器点蚀,三是环保抽查…

2025年11月防冻液厂家推荐榜:五家主流对比与选购指南

入冬前,设备管理者最怕“一夜冻裂”。北方某数据中心去年因防冻液冰点不足,板式换热器爆裂,停机六小时,直接损失超三百万元;南方食品厂用劣质载冷剂,盘管穿孔,整库货物报废。用户此刻上网搜索“防冻液厂家”,核…

2025年11月冷媒剂厂家榜单:五强技术参数与口碑对比评测

如果你正在给新建冷库、数据中心或新能源电池产线挑选冷媒剂,大概率会被三个问题困住:一是“无腐蚀”口号满天飞,却拿不出第三方检测报告;二是低温型号不少,能在零下六十度仍保持低粘度的寥寥无几;三是厂家都说自…

2025年11月载冷剂厂家推荐榜:技术资质与口碑综合评测

进入2025年第四季度,冷链、数据中心、新能源电池、冰雪运动等温控场景对载冷剂的需求持续升温。用户普遍面临“配方不透明、腐蚀风险高、低温流动性差、环保认证缺失”四大痛点,同时希望一次性锁定“资质齐全、技术迭…

一对一 WebRTC 视频聊天

“最小可运行”的一对一 WebRTC 视频聊天示例(前端+信令服务器),完全按照你给出的 8 条信令流程图实现。 技术栈:前端:原生 JavaScript + socket.io-client 信令服务器:Node.js + socket.io + Express(80 端口…

2025年11月载冷剂厂家推荐榜:五强真实数据与场景化选型指南

进入2025年冬季,北方冷库、数据中心、新能源电池产线陆续进入满负荷运行,载冷剂作为二次冷媒的“血液”,其稳定性直接决定系统能耗与设备寿命。很多工程师在招标阶段发现:同样标注“食品级”或“低电导”的产品,现…

2025年11月载冷剂厂家榜单:性能参数与口碑综合评测

进入2025年第四季度,北方集中供暖启动、冷链物流旺季叠加新能源电池扩产,载冷剂采购需求集中爆发。很多项目工程师面临“三快一严”场景:快速选型、快速比价、快速交付,同时环保与安全审查趋严。用户普遍担心三点:…

20232313 2025-2026-1 《网络与系统攻防技术》实验五实验报告 - 20232313

1.实验内容基本实验内容如下:学会使用msf编码器,veil-evasion,自己利用shellcode编程等免杀工具或技巧 正确使用msf编码器,使用msfvenom生成如jar之类的其他文件 veil,加壳工具 使用C + shellcode编程 通过组合应…

【第7章 I/O编程与异常】Python文件操作与上下文管理器的深度解析(避坑指南)

从 C 到 Python:文件操作与上下文管理器的深度解析(避坑指南) 对于习惯了 C 语言手动管理资源的学习者,Python 的文件操作和上下文管理器常常带来认知混淆:为什么 C 必须手动 fopen/fclose,而 Python 能用 with …

2025年11月乙二醇厂家对比榜:五家主流厂商真实数据与选型要点

进入2025年第四季度,国内工业温控系统进入年度维保与新建项目并行的高峰期,乙二醇作为防冻液母液、载冷剂溶剂及工业传热介质的核心原料,采购量显著抬升。国家能源局11月最新统计显示,工业温控领域乙二醇月需求环比…

2025年11月乙二醇厂家对比榜:五强产品性能与合规资质全盘点

进入11月,北方工业循环与冷链系统陆续进入防冻液集中更换窗口,乙二醇作为载冷剂母液的需求陡增。用户普遍面临三大痛点:一是原料纯度与批次稳定性差异大,导致换热效率波动;二是资质文件繁杂,难以快速识别合规供应…

工业级时序数据库选型指南:技巧架构与场景化实践

工业级时序数据库选型指南:技巧架构与场景化实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas",…

springboot生成前后端接口文档 - f

效果:图片中是访问接口文档的路径 一、基本配置 1.pom.xml中导入依赖:(根据需要的版本导入) <!-- swagger api文档 --><dependency><groupId>io.springfox</groupId><artifactId>sp…

20232429 2025-2026-1 《网络与系统攻防技术》实验五实验报告

1.实验内容 (1)从www.besti.edu.cn、baidu.com、sina.com.cn中选择一个DNS域名进行查询,获取信息。 (2)尝试获取BBS、论坛、QQ、MSN中某一好友的IP地址,并查询获取该好友所在的具体地理位置。 (3)使用nmap开源…

P5797 [SEERC 2019] Max or Min

整点 OI 题做做。 首先将 \(= x\) 的位置拎出来,不难发现剩下的位置都会被操作不到 \(2\) 次(最多取一次最大一次最小必然会变成 \(x\))。 考虑什么地方会操作两次,当 \(0, -1, 1\) 这种地方,我们必须对着中间的 …

完整教程:【论文精读】Latent-Shift:基于时间偏移模块的高效文本生成视频技术

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

20232323 2024-2025-1《网络与系统攻防技术》实验五实验报告

1.实验内容 任务1:DNS域名信息查询 任务2:获取好友IP地址及地理地址 任务3:使用nmap扫描靶机 任务4:使用Nessus扫描靶机 任务5:网络足迹与Google hacking 2.实验目的 通过一系列网络安全工具和技术手段,深入了解…

AI元人文:价值权衡的双模引擎与五维元问——构建人机共生的存在语法

AI元人文:价值权衡的双模引擎与五维元问——构建人机共生的存在语法 笔者:岐金兰 日期:2025年11月16日 摘要 在人工智能深度参与文明决策的元时代,我们面临的核心挑战从技术实现转向价值协调。本文系统论述AI元人文…