
はじめに
こんにちは。商品基盤部の藤本です。
私たちのチームでは、Spring Bootで実装したJavaアプリケーションの起動時間の短縮に取り組んでいます。今回の記事では、Class Data Sharing(以下、CDS)を本番で稼働しているアプリケーションに実際に適用した内容を紹介します。
導入時には、Datadog Java Agentとの両立という課題にも直面しました。そのため、トレースとメトリクスの送信をOpenTelemetryとMicrometerに置き換える対応もあわせて実施しました。
本記事では、CDSの概要、導入効果、導入手順、Datadogの問題とOpenTelemetryへの移行までを順に説明します。
環境
今回の取り組みは次の環境で実施しました。
- Java 21 (Eclipse Temurin)
- Spring Boot 3.5
Class Data Sharing(CDS)とは
CDSは、クラスメタデータをjsa(Java Shared Archive)ファイルとして保存し、起動時に再利用する仕組みです。クラスロード時の処理を省略できるため、起動時間とメモリ使用量の改善が期待できます。
CDSには次の2種類があります。
- Default CDS
- JDK標準ライブラリのクラスを対象とする方式
- Application CDS
- アプリケーションクラスも対象に含める方式
各ベンダーから配布されているJDKを使用する場合は、通常はDefault CDSが有効になっています。一方、jlinkで作成したカスタムJREではjsaファイルが含まれておらず、そのままではCDSが機能しません。今回の取り組みではこの点も考慮して、カスタムJRE向けのCDS生成も実施しました。
CDS導入の効果
実際にApplication CDSを導入し、本番環境で導入前後を比較しました。比較対象は起動時間と、クラスロードの影響でレイテンシが高くなりがちな初回のエンドポイント実行時間です。計測結果は次のようになりました。
| 指標 | 導入前 | 導入後 | 改善率 |
|---|---|---|---|
| 起動時間 | 6.440 s | 3.354 s | 47.92%短縮 |
| 初回のエンドポイント実行時間 | 371 ms | 165 ms | 55.53%短縮 |
この数値は本番環境で10回計測した値を平均したものです。この結果から、起動時間だけでなく初回アクセス時のレイテンシも改善できることを確認できました。
導入手順
ここからは、実際に適用した手順を説明します。
カスタムJRE向けのDefault CDSの生成
カスタムJREでCDSを使用するには、jlinkでJREを作成した後に-Xshare:dumpを実行してCDSアーカイブを生成します。
jlink --add-modules java.base,java.compiler --output /javaruntime /javaruntime/bin/java -Xshare:dump -XX:+UseCompressedOops /javaruntime/bin/java -Xshare:dump -XX:-UseCompressedOops
このコマンドを実行すると、classes.jsaとclasses_nocoops.jsaという名前でアーカイブが生成されます。実行環境によって参照先アーカイブが変わるため、UseCompressedOopsの有無で両方を生成しておくことをおすすめします。
Application CDS(Dynamic CDS archive)の生成
次に、アプリケーション終了時のロード済みクラス情報からjsaファイルを生成します。以前のApplication CDSでは、手動でクラスリストを作成してからアーカイブを作る手順が必要でした。現在はDynamic CDS archiveという方式により、アプリケーションを終了した時点のロード済みクラスからjsaファイルを自動生成できます。この方式はJDK 13で導入されています。
Spring Framework 6.1以降では、-Dspring.context.exit=onRefreshを使ってアプリケーションを起動直後に終了できます。
java \ -XX:ArchiveClassesAtExit=/path/to/application.jsa \ -Dspring.context.exit=onRefresh \ -jar my-app.jar
CDSが正しく有効になっているか確認するには、-Xlog:cdsオプションを使ってCDSの読み込み状況をログへ出力します。別の方法として、-Xshare:onを使ってCDSの使用を強制できます。-Xshare:onを指定すると、jsaファイルを正しく読み込めない場合はアプリケーションの起動が失敗する点に注意してください。
java \ -Xshare:on \ -XX:SharedArchiveFile=/path/to/application.jsa \ -jar my-app.jar
Datadogの問題とOpenTelemetryへの移行
Application CDS導入時に最も大きかった課題は、Datadog Java Agentとの両立でした。Datadog Java Agentは実行時にクラスパスへクラスを追加するため、CDSが前提とする「生成時と実行時の整合性」が崩れてしまいます。
この挙動はGitHub上でもIssueとして報告されています。
なお、このIssueは2026年1月にクローズされ、Datadog側の方針として「CDSをサポートする」方向ではなく「JEP 483(Ahead-of-Time Class Loading & Linking)の利用を推奨する」ことが示されています。コメントでは、Java 25以降でdd-java-agentとJEP 483を組み合わせる利用方法が案内されています。
Datadog Java AgentがCDSに対応していないため、トレースとメトリクスの送信方式を次の構成に移行しました。
- トレース:OpenTelemetry(OTLP)→ Datadog Agent
- メトリクス:Micrometer(DogStatsD)→ Datadog Agent
送信先をDatadog AgentではなくOpenTelemetry Collectorに変更することも検討しましたが、構成や運用の変更にかかるコストの観点から今回は見送りました。
OpenTelemetryによるトレース送信
OpenTelemetryでトレースを送信するために、アプリケーションへopentelemetry-spring-boot-starterを導入しました。Spanの開始と終了はHandlerInterceptorで制御しながら、送信内容が重複しないようspring-webmvcの自動計装は無効化しました。
otel: instrumentation: spring-webmvc: enabled: false exporter: otlp: endpoint: "http://${DD_AGENT_HOST:localhost}:4318" traces: exporter: otlp sampler: always_on
Datadog Agent側では、OTLP受信を有効化します。
DD_OTLP_CONFIG_RECEIVER_PROTOCOLS_HTTP_ENDPOINT=0.0.0.0:4318 DD_OTLP_CONFIG_RECEIVER_PROTOCOLS_GRPC_ENDPOINT=0.0.0.0:4317
Micrometerによるメトリクスの送信
メトリクスはmicrometer-registry-statsdを追加し、DogStatsD形式でDatadog Agentへ送信できるようにします。
management: statsd: metrics: export: enabled: true flavor: datadog host: ${DD_AGENT_HOST:localhost} port: 8125
Spanの属性のマッピング調整
OpenTelemetryで送ったトレースがDatadogで期待通りに絞り込めない場合は、属性名の差分が原因になることがあります。目的のトレースを検索できるようにするため、たとえば次のようなDatadog上の属性を付与する必要があります。Datadogのマッピング表を参照し、必要な属性をSpan属性とResource属性へ明示的に設定しました。
- Span属性:
http.method、http.urlなどのリクエストごとに変わる値 - Resource属性:
service.name、env、container.nameなどの共通値
メトリクスについても、ダッシュボードで計測している指標に合わせて、メモリやGCに関する収集項目を追加し、メトリクス名やタグを調整しました。
おわりに
本記事では、Application CDSを活用して起動時間を短縮する取り組みと、Datadog Java Agentとの両立課題への対応を紹介しました。実測では、起動時間と初回エンドポイント実行時間の両方で改善を確認できました。
CDSはオプション追加だけで始められますが、カスタムJRE、暖機処理、監視ライブラリの挙動まで含めて設計することで効果を最大化できます。Javaアプリケーションの起動時間に課題がある方は、まずは小さな範囲で計測しながら段階的に導入してみてください。
現在はJava 25とSpring Boot 4への移行を進めており、その後でAOT Cacheの導入を予定しています。引き続き、運用環境に即した形でパフォーマンス改善の取り組みを進めていきます。
ZOZOでは、一緒にサービスを盛り上げてくれる仲間を募集中です。興味のある方は以下の採用情報をご確認ください。