アプリケーション・クラス・データ共有機能について
アプリケーション・クラス・データ共有機能(以下、AppCDS機能)は、Oracle版Javaには以前からあり、Java8での商用版で利用できました。
そして、OpenJDKではJava10から利用できるようになっています。Java12、Java13では、さらに利用しやすいように改善されています。
アプリケーション・クラス・データ共有機能とは
Javaプログラムは、実行時にクラスファイルを各プラットフォームで利用できるように変換してから実行されます。AppCDSでは、この工程を事前に行い、ファイルとして保存しておくことで次回の実行時にはその工程をスキップできます。
そのため、起動時間の短縮が可能になり、また、複数のJVMで共有することもできるため、リソースも節約できます。クラス・データ共有について詳しく知りたい方は、Java仮想マシン・ガイド により詳しく説明されているので、そちらを参照してください。
Java12からのAppCDS機能の利用方法
通常の流れでは、以下のような3つの手順を経て、準備から実行までをします。
- クラス・データ共有として利用するクラス一覧ファイルを準備する
- クラス一覧ファイルから、CDSアーカイブファイルを作成する
- 作成したCDSアーカイブファイルを指定して、プログラムを実行する
Java10からと作成方法は同様ですが、これまで必要だったオプションがいらなくなり、多少作成方法が変わっています。まず、クラス・データ共有として利用するクラス一覧ファイルは、リスト5のように実行して作成します。
java -Xshare:off -XX:DumpLoadedClassList=class_list.txt \ //(1)アーカイブするクラスファイル一覧を作成 //(2)以下、通常のプログラムを実行する際に指定するパラメータを指定 -cp build/libs/main.jar:build/export/* \ -Dloader.main=jp.enbind.sample.MainApplication jp.enbind.sample.MainLauncher
(1)のように「-Xshare:off」を指定し、クラス・データ共有を無効にします。そして「-XX:DumpLoadedClassList」では、出力するアーカイブするクラス一覧ファイルを指定します。これ以降のオプションについては、(2)のように通常、プログラムを実行する際と同じ指定をします。また、このコマンドを実行して作成されたファイルがリスト6です。
java/lang/Object java/lang/String java/io/Serializable java/lang/Comparable java/lang/CharSequence java/lang/constant/Constable // ・・・省略 jp/enbind/sample/MainLauncher org/springframework/boot/loader/PropertiesLauncher org/springframework/boot/loader/Launcher // ・・・省略
このファイルを見ると、Javaの基本クラスや利用している外部ライブラリや、今回作成したクラスなどが含まれていることがわかります。このファイルを編集して、任意のクラスを追加することや除外することも可能です。
ただし、想定するクラスが含まれていない場合には、実行される形式に問題がある場合があります。例えば、今回用意したサンプルプログラムはSpringBootを用いたものですが、Jarファイル内に依存するJarファイルが含まれるような特別な形式になっています。
そのような形式では、CDS機能を生かすことができません。そこで、今回のサンプルでは、通常のJarファイル形式にしています。続いて、リスト7は作成したクラス一覧ファイルを使って、CDSアーカイブファイルを作成する場合の実行例です。
java -Xshare:dump -XX:SharedClassListFile=class_list.txt -XX:SharedArchiveFile=cds_archive.jsa \ //(1)必要なオプションの指定 // 以下、通常のプログラムを実行する際に指定するパラメータを指定 -cp build/libs/main.jar:build/export/* \ -Dloader.main=jp.enbind.sample.MainApplication jp.enbind.sample.MainLauncher
(1)のように「-Xshare:dump」は、作成されるCDSアーカイブに関する診断情報を出力します。そして、「-XX:SharedClassListFil」には先ほど作成したクラス一覧ファイルを指定し、「-XX:SharedArchiveFile」に出力するCDSアーカイブファイル名を指定します。
このコマンドを実行するとログが出力され、実際に作成されたクラス状況がリスト8のように結果が表示されます。ここでは、合計で5142個クラス定義がCDSアーカイブファイルに含まれたことがわかります。
// ・・・省略 Number of classes 5142 instance classes = 5068 obj array classes = 66 type array classes = 8 Updating ConstMethods ... done. Removing unshareable information ... done. // ・・・省略
そして、CDSアーカイブファイルを使って実行する場合には、リスト9のようなオプションを指定して実行します。
java -Xshare:on -XX:SharedArchiveFile=cds_archive.jsa \ //(1) 必要なオプションの指定 -cp build/libs/main.jar:build/export/* \ -Dloader.main=jp.enbind.sample.MainApplication jp.enbind.sample.MainLauncher
(1)のように、「-Xshare:on」は、CDSアーカイブを利用するための指定です。デフォルトで、CDSアーカイブが利用できる場合にはonになるので、この指定は省略しても構いません。「-XX:SharedArchiveFile」には作成されたCDSアーカイブファイルを指定します。
アプリケーション終了時でのCDSアーカイブファイルの自動生成
ここまでの流れから、共有アーカイブファイルを作成するのは面倒という印象を持つ方もいることでしょう。そこでJava13からは、もう少し簡単に共有アーカイブファイルが作成できるようになりました。具体的には、クラス一覧ファイルを作成することなく、共有アーカイブファイルが作成できるようになっています。そのための実行例がリスト10です。
java -XX:ArchiveClassesAtExit=cds_archive.jsa \ //(1)必要なオプションの指定 -cp build/libs/main.jar:build/export/* \ -Dloader.main=jp.enbind.sample.MainApplication jp.enbind.sample.MainLauncher
(1)のように「-XX:ArchiveClassesAtExit」オプションで作成するCDSアーカイブファイルを指定して実行します。そして、プログラム終了時に共有アーカイブファイルが作成されます。また、CDSアーカイブファイルを利用して実行する際には、先ほどと変わりありません。
実行時間について
実際に、クラスデータ共有機能を利用しない場合と何も指定しない場合、そして、それぞれの方法でアプリケーション・クラス・データ共有を利用した場合の実行時間をMacOS上で計測した結果が図1です。
(1)は、起動時に「-Xshare:off」を指定し、意図的にCDS機能を利用しない場合の起動時間です。そして(2)は、AppCDS機能は利用せず、デフォルトのCDS機能のみを使った場合です。(3)は、Java12でのクラス一覧ファイルを使って作ったCDSアーカイブファイルを使い、(4)はJava13での「-XX:ArchiveClassesAtExit」で作成されたCDSアーカイブファイルを使って実行した時の起動時間です。
今回は、(3)と(4)で時間は変わりませんでしたが、必ずしも同じ結果とならないので、パフォーマンスがうまく出る方法を選択してください。ただし、実際の時間は実行するハードスペックやOSごとに特性は変わります。
また、実際の時間計測として利用したプログラムはリスト11のようにmain処理までにかかった時間(ミリ秒)です。ここでのサンプルアプリケーションは、SpringBootとJavaFXを用いたアプリケーションであり、実際にはここからさらにさまざまなクラスがロードされ、アプリケーションの開始まで時間がかかります。AppCDSを利用すれば、Javaでの起動部分も短くなるので、さらに効果が見込めるはずです。
public static void main(String[] args) throws Exception { long time = ManagementFactory.getRuntimeMXBean().getUptime(); // ・・・・省略 }
アプリケーション・クラス・データ共有を使った、アプリケーション全体の起動時間の短縮でも、数100ミリ秒から多くても1、2秒程度しか違いありません。そのため、Javaを常駐型のサービス実行されるような利用ケースではあまりメリットは享受できないと思います。しかし、今後サーバレスのようなオンデマンド型のマイクロサービスへの需要が高まれば、こういった最適化への改善や知見がより重要になってくるに違いありません。
Packaging Tool - Javaアプリケーションのインストーラを作成
JavaアプリケーションのJVMを含んだ形のインストーラを作成することができるツールが用意されました。このようなツールを利用することで、アプリケーション利用者がJava環境を意識することなく、通常のアプリケーションと同様に配布が行えます。例えば、MacOS用に作成したアプリケーションは、図2のようなOSに準じたインストーラが利用できます。
jpackage --type pkg --name myJavaApp --input build/libs --main-jar main-app-1.0.jar --icon assets/myJavaApp.icns
リスト12で使用したオプションは表2で記すオプションを利用しています。その他にも多くのオプションがあるので、詳細はこちらを参照してください。
ただし、Windows用のパッケージを作成する場合には、別途、WIX TOOLSETが必要になります。また、サンプルコードでは、SpringBootとJavaFXを使ったアプリケーションをGradle(jpackage/build.gradle)でパッケージの作成まで行ったものがあるので、そちらも参考にしてください。
引数 | 説明 |
---|---|
type | 作成するパッケージ形式。Windowsの場合にはmsiもしくはexe形式。MacOSの場合にはdmgもしくはpkg形式。 |
name | アプリケーション名。 |
input | jarファイルを格納しているディレクトリ。 |
main-jar | メインとなるjarファイル。 |
icon | アプリケーションのアイコン(Windowsであればicoファイル、MacOSであればicnsファイル)。 |
またjlinkなども合わせて利用することで、配布する容量などの圧縮なども行えるので、より配布しやすいパッケージが作成できるはずです。ただし、まだIncubatorというステータスのために、今後正規リリースされる際には利用できるコマンドなども変わるかもしれませんのでご注意ください。
Remove the Nashorn JavaScript Engine - Java Scriptエンジンの削除
Java8で導入されたOracle Nashornですが、Java11(JEP335)でDeprecatedになりました。そして、Java15では削除されています。従って、Java15以降でJavaScriptエンジンを使う場合にはGraalVMなどを使う必要があります。
まとめ
Java12〜Java15での変更点は、Java9やJava11の時に比べると小さい変更と言えます。しかし、Java開発者にとっては、言語仕様にかかわる部分も多いため、知っておくべき変更と言えます。
そして、実行環境としてのJavaという意味ではよりネイティブプログラムに近い制約でも動くように意識されていると思われます。また、さらに起動時間などを小さくするためには、GraalVMというJava以外でも利用できるVMがあり、そちらを利用することでJavaをネイティブバイナリに変換することも可能です。
このように、今後Javaは開発言語としての機能だけではなく、実行環境としての変化はより大きくなってくると思われます。その際には、Javaだけのリリース内容だけではなく、GraalVMやOpenJ9などJavaVM側の選択肢も検討することがより重要になります。