Application Class-Data Sharing
启动 Java 应用时,JVM 需要加载大量类。每次启动都重新加载,浪费时间。
JDK 5 引入了 CDS(Class-Data Sharing),让 JVM 把核心类打包成一个共享存档,启动时直接加载,不用重新解析。
JDK 10 把 CDS 扩展为 AppCDS(Application Class-Data Sharing),应用也能享受这个加速。
原理
┌─────────────────────────────────────────────────────────────────┐
│ 无 CDS 启动 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ JVM 启动 │
│ ↓ │
│ 读取 classpath 上的每个 JAR │
│ ↓ │
│ 解析每个 class 文件(字节码 → 内部结构) │
│ ↓ │
│ 加载到内存 │
│ │
│ 耗时:3-10 秒(取决于应用大小) │
│ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 有 CDS 启动 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ JVM 启动 │
│ ↓ │
│ 映射共享存档文件到内存(mmap) │
│ ↓ │
│ 类数据已解析完成,直接使用 │
│ │
│ 耗时:0.5-1 秒 │
│ │
└─────────────────────────────────────────────────────────────────┘优势:
- 启动更快:不用重新解析类
- 内存共享:多个 JVM 进程可以共享同一块内存
- 减少 I/O:存档在内存中,不需要每次读取 class 文件
基本使用
步骤一:创建存档
bash
# 方式一:静态存档(手动指定类)
java -Xshare:archive=on \
-XX:SharedArchiveFile=app.jsa \
-cp your-app.jar \
your.app.Main
# 方式二:动态存档(JDK 10+)
# 在应用退出时自动创建存档
java -XX:ArchiveClassesAtExit=app.jsa \
-cp your-app.jar \
your.app.Main步骤二:使用存档
bash
# 使用存档启动
java -XX:SharedArchiveFile=app.jsa \
-cp your-app.jar \
your.app.Main动态存档
JDK 10+ 支持动态存档,记录应用实际运行中加载的类:
bash
# 第一次运行:创建存档
$ java -XX:ArchiveClassesAtExit=app.jsa \
-cp your-app.jar \
your.app.Main
# 输出:应用正常运行,退出时生成 app.jsa
# 后续运行:使用存档
$ java -XX:SharedArchiveFile=app.jsa \
-cp your-app.jar \
your.app.Main完整示例
创建存档脚本
bash
#!/bin/bash
# build-appcds.sh
APP_JAR="target/myapp.jar"
ARCHIVE_FILE="app.jsa"
echo "=== Step 1: 运行应用并创建存档 ==="
java -XX:ArchiveClassesAtExit=$ARCHIVE_FILE \
-cp $APP_JAR \
com.myapp.Main
echo "=== 存档创建完成 ==="
ls -lh $ARCHIVE_FILE
echo "=== Step 2: 验证存档 ==="
java -XX:SharedArchiveFile=$ARCHIVE_FILE \
-cp $APP_JAR \
-Xlog:class+load=info \
com.myapp.Main 2>&1 | head -20对比启动时间
bash
#!/bin/bash
# benchmark.sh
APP_JAR="target/myapp.jar"
echo "=== 无 CDS 启动 ==="
time java -cp $APP_JAR com.myapp.Main
echo ""
echo "=== 有 CDS 启动 ==="
time java -XX:SharedArchiveFile=app.jsa \
-cp $APP_JAR \
com.myapp.Main典型结果:
| 方式 | 启动时间 | 加速 |
|---|---|---|
| 无 CDS | 5.2 秒 | — |
| 有 CDS | 1.8 秒 | 2.9x |
与 Spring Boot 结合
Spring Boot 应用使用 AppCDS:
bash
# 创建存档
java -XX:ArchiveClassesAtExit=spring.jsa \
-Djdk.module.illegalAccess=permit \
-jar target/myapp.jar \
--spring.main.web-application-type=none
# 使用存档启动
java -XX:SharedArchiveFile=spring.jsa \
-Djdk.module.illegalAccess=permit \
-jar target/myapp.jarMaven 插件配置
xml
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<jvmArguments>
-XX:ArchiveClassesAtExit=target/app.jsa
</jvmArguments>
</configuration>
</plugin>生产环境注意事项
存档兼容性
存档必须与应用版本匹配:
bash
# ❌ 应用更新后继续用旧存档
java -XX:SharedArchiveFile=old.jsa -cp new-app.jar ...
# ✅ 应用更新后需要重建存档监控存档使用
bash
# 查看存档信息
java -XX:PrintSharedArchive -XX:SharedArchiveFile=app.jsa
# 列出存档中的类
java -XX:+UnlockDiagnosticVMOptions \
-XX:SharedArchiveFile=app.jsa \
-XX:PrintSharedClassList存档大小
bash
$ ls -lh app.jsa
-rw------- 1 user 120M app.jsa
# 存档大小取决于应用依赖
# 小应用:几十 MB
# 大应用:几百 MBJDK 21 增强
JDK 21 的 JEP 423 引入了区域注册机制改进,让 AppCDS 更加高效:
bash
# JDK 21+:自动选择存档策略
java -XX:ArchiveClassesAtExit=app.jsa \
-cp your-app.jar \
your.app.Main
# 存档会自动包含更多类,提升效果更好小结
AppCDS 的价值在于加速应用启动:
| 场景 | 效果 |
|---|---|
| 微服务冷启动 | 减少 2-5 秒 |
| Serverless 函数 | 显著减少冷启动时间 |
| Docker 容器 | 镜像启动更快 |
| CI/CD 流水线 | 构建产物验证更快 |
什么时候用:
- 应用启动频率高,需要快启动
- Docker/Kubernetes 环境
- Serverless/FaaS 平台
- 微服务架构
什么时候不用:
- 启动时间不是瓶颈
- 应用经常更新,存档维护成本高
- 内存受限环境
