Skip to content

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 秒                                                 │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

优势:

  1. 启动更快:不用重新解析类
  2. 内存共享:多个 JVM 进程可以共享同一块内存
  3. 减少 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

典型结果:

方式启动时间加速
无 CDS5.2 秒
有 CDS1.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.jar

Maven 插件配置

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
# 大应用:几百 MB

JDK 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 平台
  • 微服务架构

什么时候不用:

  • 启动时间不是瓶颈
  • 应用经常更新,存档维护成本高
  • 内存受限环境

基于 VitePress 构建