Java CDS 怎样缩短应用启动时间
Java CDS(Class Data Sharing)缩短启动时间的关键,不是把 Java 程序提前编译成另一种语言,而是让多个 JVM 复用已经整理好的类元数据。Oracle JDK 默认会尝试使用 CDS 归档;如果应用启动还要加载大量固定的业务类,可以再制作 AppCDS 动态归档,把应用类也纳入复用范围。
官方地址:https://docs.oracle.com/en/java/javase/24/vm/class-data-sharing.html
最小落地路径是:先保持生产启动命令不变运行一次,用-XX:ArchiveClassesAtExit生成动态归档;再用同一套 JDK、类路径和启动参数,通过-XX:SharedArchiveFile加载它。归档是启动优化的补充,不能替代应用本身的初始化治理。
先分清默认 CDS 和 AppCDS
默认 CDS 主要覆盖 JDK 核心类。JVM 启动时可以把归档中的只读元数据映射进进程,让多个 JVM 共享这部分内容;从 JDK 12 起,Oracle JDK 二进制通常会预置默认 CDS 归档。默认功能已经启用时,不需要先写一套复杂脚本。
当应用相对较小、但每次启动都要重复加载相同的业务类时,AppCDS 才更值得考虑。它把应用类路径中的选定类纳入归档,适合启动命令、依赖版本和运行方式都比较稳定的服务。

用一次试运行生成动态归档
动态归档的优点是步骤更少,通常也不必重复保存默认归档已经覆盖的类。下面假设应用由固定的 app.jar 启动,先用与生产一致的命令跑一遍,让 JVM 记录这次实际加载到的类:
# 用与生产一致的类路径运行一次,并在退出时写出动态 CDS 归档
java -XX:ArchiveClassesAtExit=app.jsa -jar app.jar
这里的 app.jsa 是归档文件名,实际部署时应放到稳定的发布目录。试运行覆盖到的代码路径越接近真实启动路径,归档越有机会覆盖稳定的启动类;但它不是把所有未来可能加载的类都预先收集进去。
如果应用的启动器很复杂,也可以让正在运行的 JVM 先以 -XX:+RecordDynamicDumpInfo 启动,再通过 jcmd 连接进程创建动态归档:
# 让 JVM 记录动态归档所需的信息;pid 替换为目标 Java 进程号
java -XX:+RecordDynamicDumpInfo -jar app.jar
# 从运行中的 JVM 导出动态归档,不改写应用自身的启动参数
jcmd VM.cds dynamic_dump app.jsa
两种方式都依赖真实的应用启动路径。不要为了“让归档更大”而随意扩大类路径,归档内容应服务于稳定的启动场景。
让后续 JVM 加载应用归档
生成归档后,后续启动命令增加 -XX:SharedArchiveFile:
# 使用动态归档启动同一个应用;类路径和 JDK 要与创建归档时保持一致
java -XX:SharedArchiveFile=app.jsa -jar app.jar
如果同时维护静态归档与动态归档,可以用平台对应的路径分隔符组合它们。Oracle 文档说明,静态归档先加载,动态归档再叠加;在 Windows 上,示例中的冒号需要换成分号:
# Linux/macOS 示例:先加载基础归档,再加载应用动态归档
java -XX:SharedArchiveFile=base.jsa:app.jsa -jar app.jar
生产上最重要的变化不是多加一个 JVM 参数,而是把归档文件和启动命令绑定到同一次发布。应用 JAR、依赖目录或启动参数变化后,应重新生成归档,而不是继续沿用旧文件。
归档为什么会失效
CDS 归档不是跨环境通用缓存。归档内容与创建它的 JDK 构建相关;类路径中的 JAR 被修改、运行时类路径与创建时不一致,或者模块相关参数变化,都可能让归档无法加载、只部分生效,甚至被禁用。
还要注意三个常见边界:带非空目录的类路径或模块路径不符合 CDS 的限制;--upgrade-module-path、--patch-module 和 --limit-modules 会禁用 CDS;-Xshare:on 适合测试归档是否能用,但官方不建议把它作为生产参数,因为归档不可用时 JVM 可能直接退出。

根据发布方式选择归档方案
| 场景 | 优先方案 | 原因 |
|---|---|---|
| 只想获得基础收益 | 保留默认 CDS | JVM 默认尝试加载核心类归档,改动最小 |
| 启动命令和依赖稳定 | ArchiveClassesAtExit + SharedArchiveFile | 能把应用启动类纳入动态归档,步骤清晰 |
| 启动脚本复杂但 JVM 可持续运行 | jcmd VM.cds dynamic_dump | 减少对启动脚本的侵入 |
| 希望按相同命令自动维护 | -XX:+AutoCreateSharedArchive | JDK 19 及之后可按归档文件和 JDK 版本自动创建或重建 |
最后不要只看单次启动耗时就宣布优化完成。至少要把归档文件作为构建产物管理,并在 JDK、依赖、类路径或模块参数变更时重新生成;如果归档被忽略,先检查启动条件是否一致,再决定是否继续保留这个参数。
相关问题
- Java CDS 默认需要手动打开吗?通常不需要,默认模式会在可用时尝试使用 CDS;可用
-Xshare:off关闭。 - AppCDS 能不能替代 JIT 优化?不能。CDS 主要减少类加载和元数据准备成本,不等于提前完成运行期 JIT 编译。
- 换了 JDK 还能继续用旧的 jsa 吗?不要直接假设可以。归档与 JDK 构建绑定,换 JDK 或关键启动条件后应重新生成并重新评估。
Spring Boot Actuator 怎样按端点暴露健康信息
- 上一篇
- Spring Boot Actuator 怎样按端点暴露健康信息
- 下一篇
- Spring WebFlux 怎样限制并发请求的背压范围
-
- 文章 · java教程 | 1小时前 | Java · java httpclient completablefuture sendAsync
- Java HttpClient 怎样用 sendAsync 读取异步响应
- 468浏览 收藏
-
- 文章 · java教程 | 1小时前 | Java · 故障排查 · java 死锁排查 jcmd Thread.print 线程栈 Thread.dump_to_file JVM线程
- Java jcmd 怎样查看运行中进程的线程信息
- 452浏览 收藏
-
- 文章 · java教程 | 1小时前 |
- Spring WebFlux 怎样限制并发请求的背压范围
- 187浏览 收藏
-
- 文章 · java教程 | 1小时前 |
- Spring Boot Actuator 怎样按端点暴露健康信息
- 389浏览 收藏
-
- 文章 · java教程 | 1小时前 | Java教程 · java 国际化 locale ResourceBundle ResourceBundle.Control
- Java ResourceBundle.Control 怎样控制语言回退链
- 118浏览 收藏
-
- 文章 · java教程 | 1小时前 | 日志 · 性能优化 · Java教程 · java 日志级别 supplier System.Logger isLoggable 惰性构造
- Java System.Logger 怎样按级别判断是否构造日志参数
- 454浏览 收藏
-
- 文章 · java教程 | 1小时前 | 反射 · Java · java JPMS privateLookupIn MethodHandles.Lookup 模块访问
- Java MethodHandles.Lookup 怎样处理模块边界下的访问权限
- 276浏览 收藏
-
- 文章 · java教程 | 1小时前 | Java · 并发编程 · java Java内存模型 VarHandle getAcquire setRelease
- Java VarHandle 怎样用 acquire 和 release 建立可见性边界
- 151浏览 收藏
-
- 文章 · java教程 | 1小时前 | Java · 正则表达式 · 字符串处理 · java 捕获组 正则替换 Matcher.replaceAll MatchResult
- Java Matcher.replaceAll 怎样按捕获组动态生成替换文本
- 193浏览 收藏
-
- 文章 · java教程 | 1小时前 |
- Java Map.copyOf 怎样构造不可变映射快照
- 175浏览 收藏
-
- 文章 · java教程 | 1小时前 | Java · nio · java 符号链接 Files.getLastModifiedTime FileTime LinkOption
- Java Files.getLastModifiedTime 怎样读取符号链接自身时间
- 298浏览 收藏
-
- 文章 · java教程 | 1小时前 |
- Java Virtual Threads 怎样避免把阻塞任务放进固定池
- 318浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 842次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 842次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 923次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 898次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 728次使用
-
- Go Java 算法之字符串解码示例详解
- 2023-01-07 479浏览
-
- Go Java算法之单词搜索示例详解
- 2022-12-30 337浏览
-
- Gojava算法之括号生成示例详解
- 2023-02-22 128浏览
-
- GoJava算法之累加数示例详解
- 2023-01-07 149浏览
-
- GoJava算法最大单词长度乘积示例详解
- 2023-01-12 202浏览

