kagamihogeの日記

kagamihogeの日記です。

JEP 310: Application Class-Data Sharingをテキトーに訳した

https://openjdk.java.net/jeps/310

JEP 310: Application Class-Data Sharing

Owner    Ioi Lam
Type    Feature
Scope   Implementation
Status  Closed / Delivered
Release 10
Component   hotspot / runtime
Discussion  hotspot dash dev at openjdk dot java dot net
Reviewed by Karen Kinnear, Mikael Vidstedt, Vladimir Kozlov
Endorsed by Mikael Vidstedt, Vladimir Kozlov
Created 2017/08/08 22:02
Updated 2018/08/17 20:35
Issue   8185996

Summary

起動時間とフットプリント改善目的で、既存のClass-Data Sharing ("CDS")でアプリケーションクラスを共有アーカイブ内に配置可能なように拡張します。

Goals

  • 異なるJavaプロセス間で共通のクラスメタデータを共有する事によりフットプリントを削減。
  • 起動時間の改善。
  • JDKランタイムイメージファイル($JAVA_HOME/lib/modules)のアーカイブクラスとプラットフォームおよびシステムクラスローダにロードされるアプリケーションクラスパスを使えるようにCDSを拡張。
  • アーカイブクラスをカスタムクラスローダにロード出来るようにCDSを拡張。

Non-Goals

  • 本実装で使用する共有クラスのアーカイブストレージフォーマットの標準化はしない。
  • 本リリースでは、CDSはユーザ定義モジュール(ex. --module-pathで定義する)のクラスはアーカイブしない。将来リリースでサポート予定。

Success Metrics

本プロジェクトは、(1)複数JVMプロセス間でJavaのクラスメタデータが使用するメモリの著しい削減(2)スタートアップ時間の著しい改善、が達成出来れば成功と考えられます。

実際に適用例としては、

上記の数値は特定のベンチマーク下であり一般的に言えるとは限りません。クラスローダがロードするクラス数およびアプリケーション全体のヒープ使用量に依存します。

Description

JDK 5で導入したClass-Data Sharingは事前にクラスを共有アーカイブにすることで起動時間改善のため実行時にメモリマッピングします。また、複数JVMが同一アーカイブファイルを共有する場合、動的にメモリフットプリントを削減します。

現行のCDSアーカイブクラスのロードにはブートストラップクラスローダのみ使用可能です。Application CDS ("AppCDS")はCDSを拡張し、ビルトインシステムクラスローダ(別名"app class loader")・ビルトインプラットフォームクラスローダ・アーカイブクラスロード用のカスタムクラスローダ、を使用可能です。

大規模エンタープライズアプリケーションのメモリ使用分析によると、アプリケーションクラスローダに数万クラスをロードします。こうしたアプリケーションにAppCDSを適用するとJVMプロセスごとに数十から数百MBを削減可能です。

サーバレスクラウドサービスの分析によると、起動時に数千アプリケーションクラスをロードします。AppCDSでサービスの起動時間短縮とシステムレスポンスタイム全体を改善できます。

Enabling AppCDS

デフォルトでは、Class-Data SharingはJVMのブートストラップクラスローダでだけ有効化されています。-XX:+UseAppCDSコマンドラインオプション指定により、システムクラスローダ(別名"app class loader")・プラットフォームクラスローダ・その他のユーザ定義クラスローダ、でクラスデータ共有を有効化します。

Determining the classes to archive

多数のクラスでアプリケーションをパッケージしても、通常の実行ではそれらの一部のみしか使用しない事が多いです。そうした使用するクラスのみのアーカイブ化により、ファイルストレージサイズと実行時のメモリ使用量を削減します。これを行うには、まず-Xshare:offでアプリケーションを通常実行する際に-XX:DumpLoadedClassListでロードされる全クラスを記録します。

注意点としてデフォルトでは-XX:DumpLoadedClassListはブートストラップクラスローダがロードするクラスのみ含みます。-XX:+AppCDSオプション指定により、システムクラスローダ・プラットフォームクラスローダがロードするクラスも含むようになります。以下は例です。

java -Xshare:off -XX:+UseAppCDS -XX:DumpLoadedClassList=hello.lst -cp hello.jar HelloWorld

Creating the AppCDS archive

AppCDSアーカイブを生成するには、-Xshare:dump -XX:+UseAppCDSオプションを指定し、-XX:SharedClassListFileにクラスのリストを指定し、アプリケーションで使用するのと同じクラスパスを指定します。また、-XX:SharedArchiveFileにクラスを格納するアーカイブファイル名を指定します。注意点として、-XX:SharedArchiveFileが未指定の場合、アーカイブされるクラスはJDKインストールディレクトリに保存されます。

$ java -Xshare:dump -XX:+UseAppCDS -XX:SharedClassListFile=hello.lst \
    -XX:SharedArchiveFile=hello.jsa -cp hello.jar

Using the AppCDS archive

AppCDSアーカイブを生成したら、アプリケーション開始時にそれを使用します。-Xshare:on -XX:+UseAppCDSオプションと、アーカイブファイル名を指定する-XX:SharedArchiveFileを指定して実行します。

$ java -Xshare:on -XX:+UseAppCDS -XX:SharedArchiveFile=hello.jsa \
    -cp hello.jar HelloWorld

Classpath mismatch

-Xshare:dumpのクラスパスは、-Xshare:onのクラスパスと同一もしくはプレフィクスの必要があります。そうでない場合、JVMはミスマッチのクラスパスのエラーメッセージを表示して停止します。ミスマッチの調査には、-Xlog:class+path=infoを追加すると、JVMは期待されるクラスパスと実際に使用されたクラスパスに関する詳細な診断情報を出力します。

Using -Xshare:auto

AppCDSの動作は固定アドレスにアーカイブをメモリマッピングします。ある種のOSでは、address space layout randomization (ASLR)が有効の場合には特に、要求アドレス空間が利用不可能な場合にメモリマッピング操作が失敗することがあります。-Xshare:onを指定する場合、JVMはこれをエラーとして扱い起動を失敗します。この場合にアプリケーションを柔軟に対処させるには、代わりに-Xshare:autoを推奨します。この場合、JVMアーカイブのメモリマッピングに失敗すると、AppCDSを無効化してアプリケーションを通常実行します。

なお、-Xshare:autoはクラスパスのミスマッチの場合にもAppCDSを無効化します。よって、まずミスマッチが無いことを-Xshare:onでテストしてから本番環境で-Xshare:autoにするのを推奨します。

Listing the Classes Loaded from the AppCDS Archive

AppCDSアーカイブからロードされたクラスを参照するには-Xlog:class+load=infoを使います。ロードされたクラス名とどこからロードされたかを表示します。CDSアーカイブからロードされたクラスはsource: shared objects fileの形で表示します。

$ java -Xshare:on   -XX:+UseAppCDS -XX:SharedArchiveFile=hello.jsa \
    -cp hello.jar -Xlog:class+load=info HelloWorld | grep HelloWorld
[0.272s][info][class,load] HelloWorld source: shared objects file

Implementation

  • Platform and system class loaders: HotSpot VMはプラットフォームとシステムクラスローダのクラスロード要求を解釈します。これらローダーがCDSアーカイブのクラスを要求すると、VMは通常のクラスファイルパースおよび検証をスキップし、アーカイブのコピーをロードします。
  • Custom class loaders: カスタムクラスローダがClassLoader::defineClassを呼ぶと、アーカイブクラスとクラスファイルのマッチングを、クラスファイルデータのフィンガープリントでマッチングを試行します。もしマッチする場合、VMはクラスファイルのパースおよび検証をスキップし、アーカイブのコピーを直接ロードします。

Alternatives

複数のJVMプロセスが動的にロードするクラスの共有に共有メモリ領域を検討しましたが、共有のポテンシャルは低く*1実装困難なことが分かりました。

その代わりに、アプリケーションクラスデータの共有をstaticにしました。

  • 'dump'のひと手間が追加。
  • アプリケーションのJARファイルを更新すると再度dumpが必要。

本機能は既存のCDS上に構築するため、実装をシンプルにして、想定ユースケースにおいて高レートの共有を実現します。

Testing

互換性の保証とパフォーマンスを確認する網羅的なテストが必要です。

テストはすべてのサポートプラットフォームで実行します。ある種のプラットフォーム(特にWindows/x86)では、Address Space Layout Randomization (ASLR)が理由でJVMアーカイブマッピングが出来ずにテストが失敗する可能性があります。

Risks and Assumptions

AppCDSはJDK 8とJDK 9のOracle JDKで実装されています。本JEPではそのソースコードをオープンなリポジトリに移行し、一般利用可能にします。AppCDSはJDK 8とJDK 9で網羅的なテストをしているため、互換性と安定性のリスクは低いです。

*1:the sharing potential to be lower