アリババDragonwell 8 入門編


このブログでは、新しく発表されたDragonwell 8、インストール方法や使い方、開発コミュニティへの参加方法などを探ります。

本ブログは英語版からの翻訳です。オリジナルはこちらからご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。

Alibaba Dragonwell 8とは

Alibaba Dragonwell 8はOpenJDKの無料配布版で、パフォーマンスの向上やセキュリティの修正など長期的なサポートをAlibaba Cloudから提供します。Alibaba Dragonwell 8は現在、X86-64/Linuxプラットフォームをサポートしています。Dragonwell 8の追加により、データセンターにおける大規模なJavaアプリケーションの展開の安定性、効率性、パフォーマンスを大幅に向上させることができます。Alibaba Dragonwell 8は、OpenJDKプロジェクトと同じライセンス条項に基づく「フレンドリーフォーク」であり、Java SE Standardと互換性があります。ユーザーは、Alibaba Dragonwell 8を使用してJavaアプリケーションを開発し、実行することができます。オープンソースのAlibaba Dragonwell 8は、アリババグループで内部的に使用されているカスタマイズされたバージョンのOpenJDKであるAJDKのオープンソース版です。AJDKは、ビジネスシナリオに基づいて電子商取引、金融、物流に特化して最適化されており、10万台以上のサーバーを収容する超大規模なアリババデータセンターで稼働しています。

Alibaba Dragonwell 8のインストール方法

現在、Alibaba Dragonwell 8はLinux x86-64プラットフォームのみをサポートし、コンパイル済みのバイナリJDKパッケージを提供しています。Alibaba Dragonwell 8をインストールするには、以下の2つのステップを実行します。

1、GitHubのAlibaba Dragonwell 8の下のダウンロードページからコンパイル済みのバイナリJDKパッケージをダウンロードします。
2、ダウンロードしたtarパッケージをインストールディレクトリに解凍します。

インストールが完了したら、アプリケーションが参照しているJAVA_HOMEをAlibaba Dragonwell 8のインストールディレクトリに向けるだけです。例えばTomcat 8.5.39を考えてみると、Alibaba Dragonwell 8上でTomcatを動作させるには、Tomcat起動時に以下のコマンドを実行するだけです。

JAVA_HOME=/path/to/dragonwell8/installation  sh tomcat/bin/catalina.sh start

Tomcatが実際にAlibaba Dragonwell 8上で動作していることを確認するには、そのJavaコマンドに-showversionパラメータを追加して、JDKのバージョン情報を表示することができます。

JAVA_HOME=/path/to/dragonwell8/installation JAVA_OPTS="-showversion" sh tomcat/bin/catalina.sh start

Tomcatを起動すると、tomcat/logs/catalina.outファイルの先頭にAlibaba Dragonwell 8のバージョン情報が表示されます。

Alibaba Dragonwell 8の使い方

本項では、Alibaba Cloud Dragonwell 8 のさまざまな機能を利用する方法について説明します。現在、Dragonwell 8のプレビュービューでは、アリババグループで内部的にフル稼働モードになっている2つの機能を提供しています。JWarmUpとJDK Flight Recorderです。どちらの機能についても、OpenJDKコミュニティにJEPまたはパッチが提出されています。上流のマージが完了する前に、Alibaba Dragonwell 8のユーザーがこの2つの機能を事前に利用できることを期待しています。

JWarmUpを使用してJavaアプリケーションを素早くウォームアップ

OpenJDKでは、実行効率を向上させるために、Javaのバイトコードを動的に最適化されたマシンコードにコンパイルするJIT(ジャストインタイム)コンパイル技術を採用しています。しかし、このコンパイルが完了する前に、Javaコードはインタプリタモードで実行されるため、比較的効率が悪いです。

アプリケーションが起動してビジネストラフィックが入ってきたばかりの時には、多くのJavaメソッドがJITを使ってコンパイルを開始し、ビジネスリクエストが遅いインタプリタによって実行されている可能性が高いです。この現象の最終的な結果として、システム負荷が非常に高くなり、多くのユーザリクエストのタイムアウトが発生します。この問題に対するこれまでの多くの解決策は、アプリケーションをウォームアップするためにシミュレートされたトラフィックを使用することです。JWarmUp機能は、この問題を解決するための新しいオプション、すなわち、Java仮想マシンの前回の実行とコンパイルの記録を使用して、実行する現在のアプリケーションをウォームアップすることを提供します。

次の図は、JWarmUpがどのように機能するかを示しています。

JWarmUpの典型的なアプリケーションシナリオは、新しいアプリケーションのバージョンをリリースすることです。

1、JWarmUpはまず、ベータ環境の1台のマシン上でJavaアプリケーションを短時間実行し、その間にJITコンパイラが行ったアクションのメタデータを記録して収集します。
2、次に、JWarmUpは、取得したメタデータを、新しいバージョンのコードを含む本番環境の各マシン/コンテナにコピーします。
3、最後に、ベータ環境で生成されたメタデータは、JWarmUpパラメータを使用して、アプリケーションを起動する過程でJITウォームアップを完了するように本番環境のマシンを誘導することにより、本番環境のマシンにロードされます。

このようにすることで、アプリケーションはユーザーからのリクエストがあったときに最高のパフォーマンスを発揮します。

ウォームアップデータの収集

ここでもTomcatを例に考えてみると、JITコンパイル中にベータ環境で生成されたメタデータを収集するために以下のコマンドラインパラメータを追加することができ、-XX:CompilationWarmUpLogfile=パラメータは生成されたJWarmUpファイルのパスを指定します。

JAVA_HOME=/path/to/dragonwell8/installation JAVA_OPTS="-XX:ReservedCodeCacheSize=512m -XX:CompilationWarmUpLogfile=$PWD/jwarmup.log -XX:+CompilationWarmUpRecording -XX:+CompilationWarmUp -XX:-TieredCompilation -XX:+DeoptimizeBeforeWarmUp -XX:CompilationWarmUpDeoptTime=30 -XX:+PrintCompilationWarmUpDetail" sh bin/catalina.sh start

これに続いて、この生成されたファイルは、OSS、SFTP、または他の方法を使用して、生産中のマシンに送信することができます。

記録されたデータを使用してJavaアプリケーションをウォームアップ

本番環境のマシンでは、以下のパラメータを使用するだけで、以前のウォームアップデータを使用してTomcatの新しいインスタンスを起動することができます。xx:CompilationWarmUpLogfile=パラメータは、ロードするJWarmUpファイルのパスを指定します。このファイルは、ウォームアップデータを収集するために、前のステップでベータ環境からコピーしておく必要があります。

JAVA_HOME=/path/to/dragonwell8/installation JAVA_OPTS="-XX:ReservedCodeCacheSize=512m -XX:CompilationWarmUpLogfile=$PWD/jwarmup.log -XX:+CompilationWarmUp -XX:-TieredCompilation -XX:+DeoptimizeBeforeWarmUp   -XX:CompilationWarmUpDeoptTime=30 -XX:+PrintCompilationWarmUpDetail" sh bin/catalina.sh start

Javaアプリケーションのパフォーマンスを分析するためにJDK Flight Recorderを使用

JFR(JDK Flight Recorder)は、JVMに組み込まれたイベントベースのパフォーマンス分析機能です。この商用機能はOracle JDK7u4から利用できるようになりました。この機能は2018年にJDK11でオープンソース化されました。ただし、JDK8ではサポートされていません。

AlibabaはRed HatやAzul、Amazonなどの企業と連携して、この機能のサポートをJDK8に追加しています。しかし、現在のところ、このパッチはOpenJDK8uにマージされていません。この機能のサポートを事前に取得できるように、Alibaba Dragonwell 8ではAlibabaが移植したJFR版を提供しています。

JFRの使い方は非常に簡単です。コマンドラインパラメータやjcmdコマンドを使ってHotSpotを制御し、パフォーマンスデータをファイルに生成するだけです。その後、オープンソースのJMCツールを使用して、生成されたファイルをグラフィックインターフェイスで開いて分析することができます。

JFRを使用してパフォーマンスデータを収集

デフォルトでは、Alibaba Dragonwell 8ではJFR機能は無効になっています。JFR機能を有効にするには、コマンドラインパラメータ-XX:+EnableJFRを追加する必要があります。Alibaba Dragonwell 8では、JFRを使用してパフォーマンスデータを収集するためのいくつかのオプションが用意されています。

アプリケーションでコマンドラインパラメータを使用して、アプリケーションが起動した直後にJFRがパフォーマンスデータの収集を開始するように指定することができます。これは、起動時の問題を診断するのに役立ちます。次の例のコードは、Java プロセスの JFR モジュールが初期化されると、1 分間 JFR データを収集し、そのデータを rec.jfr という名前のファイルに返します。

JAVA_HOME=/path/to/dragonwell8/installation JAVA_OPTS="-XX:+EnableJFR -XX:StartFlightRecording=duration=1m,filename=rec.jfr" sh bin/catalina.sh start

また、-XX:+EnableJFRだけでもアプリケーションに追加して、アプリケーション起動後の任意のタイミングでjcmdコマンドを使ってデータを収集することができます。

例えば、以下のようなコマンドでTomcatを起動することができます。

JAVA_HOME=/path/to/dragonwell8/installation JAVA_OPTS="-XX:+EnableJFR" sh bin/catalina.sh start

解析用のデータを収集するには、解析先のTomcatプロセスのPIDを使用して、JFRの対応するjcmdコマンドを実行するだけです。例えば、Tomcatでは、以下のコマンドを使用して、特定の時点から10秒間のデータを収集します。

$ ps ax | grep tomcat
 77522 pts/18   Sl+    0:08 /home/chuansheng.lcs/dw_test/apache-tomcat-8.5.39/../j2sdk-image/bin/java -Djava.util.logging.config.file=/home/chuansheng.lcs/dw_test/apache-tomcat-8.5.39/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -XX:+EnableJFR -Djdk.tls.ephemeralDHKeySize=2048 -Djava.protocol.handler.pkgs=org.apache.catalina.webresources -Dorg.apache.catalina.security.SecurityListener.UMASK=0027 -Dignore.endorsed.dirs= -classpath /home/chuansheng.lcs/dw_test/apache-tomcat-8.5.39/bin/bootstrap.jar:/home/chuansheng.lcs/dw_test/apache-tomcat-8.5.39/bin/tomcat-juli.jar -Dcatalina.base=/home/chuansheng.lcs/dw_test/apache-tomcat-8.5.39 -Dcatalina.home=/home/chuansheng.lcs/dw_test/apache-tomcat-8.5.39 -Djava.io.tmpdir=/home/chuansheng.lcs/dw_test/apache-tomcat-8.5.39/temp org.apache.catalina.startup.Bootstrap start
 98451 pts/22   S+     0:00 grep --color=auto tomcat

$ dragonwell8_home/bin/jcmd 77522 JFR.start duration=10s filename=$PWD/rec3.jfr
77522:
Started recording 3. The result will be written to:

/home/my/workdir/rec3.jfr

10秒後、/home/my/workdir/rec3.jfr JFRファイルが生成されます。そのファイルをJMCで解析することができます。

また、JFRを直接起動してデータ収集期間を指定せずにデータを収集し、必要に応じて生成されたデータをすべて手動でファイルにダンプすることもできます。

$ dragonwell8_home/bin/jcmd 2823 JFR.start filename=$PWD/rec4.jfr
2823:
Started recording 4. No limit specified, using maxsize=250MB as default.

Use JFR.dump name=4 to copy recording data to file.

$ dragonwell8_home/bin/jcmd 2823 JFR.dump name=4 filename=rec4.jfr
2823:
Dumped recording "Recording-4", 332.7 kB written to:

/path/to/my/workdir/rec4.jfr

パフォーマンス分析にJMCを使用

JFRは、Javaアプリケーションのパフォーマンスデータをバイナリファイルに記録します。JMC (JDK Mission Control) を使用すると、グラフィックインターフェースで特定のパフォーマンスデータのセットを分析することができます。JMCはオープンソースのツールです。Alibaba Dragonwell 8には含まれていません。このツールを使用するには、OpenJDK公式サイト https://jdk.java.net/jmc/ からダウンロードしてください。

なお、Alibaba Dragonwell 8を使用して生成されたJFRデータファイルを解析するには、JMC 7.0以降が必要です。

JMCを開いた後、左側の特定の項目をクリックすると、サンプリング中に発生したイベントを詳細に解析することができます。

診断とデバッグのサポート

Alibaba Dragonwell 8には、後述するいくつかの便利な診断機能も組み込まれています。

ラージオブジェクトの割り当てに関するアラート機能

新しいJVMパラメータ-XX:ArrayAllocationWarningSize=は、この目的のために使用することができます。例えば、以下のコードでは、比較的大きな配列を割り当てています。

public static void main(String[] args) {
    doAlloc(32 * 1024 * 1024 + 1);
}
private static Object doAlloc(int size) {
    return new byte[size];
}

コード実行時にArrayAllocationWarningSizeオプションを追加すると、この配列が割り当てられている間に実行されているJavaスタックを表示します。

ParNew GCログの詳細サポート

デフォルトでは、Alibaba Dragonwell 8ではCMS(Concurrent Mark Sweep)アルゴリズムが使用されており、young generationではParNewアルゴリズムが使用されています。そのため、ParNew GCログには2つの組み込み強化機能が用意されています。

  • 次のYoung GCの最後にyoung generation用のオブジェクト型ヒストグラムを印刷できるように、jinfoツールを使用してPrintYoungGenHistoAfterParNewGCオプションを設定することができます。これを行うには、以下のコマンドを使用します。
jinfo -flag +PrintYoungGenHistoAfterParNewGC <pid>

この出力操作が完了した後、出力が多すぎるのを防ぐために、このオプションは false に戻されます。以下の内容は、典型的な出力例です。

  • -XX:+PrintGCRootsTraceTime を使用すると、GC ルートセットの種類ごとの処理にかかる CPU 時間の詳細を印刷することができます。以下の内容は出力例です。

HeapDumpsの合理化をサポート

Alibaba Dragonwell 8のjmapツールでは、新しいダンプオプションminiがサポートされており、HeapDumpの生成時に元の型のすべての配列の内容をスキップできるようになっています。このオプションを使用すると、生成されるHeapDumpファイルのサイズが大幅に削減され、型とオブジェクトの関係性のチェックだけが必要なシナリオで特に役立ちます。

次の図にコードサンプルを示します。

アリババ・ドラゴンウェルコミュニティの構築に参加

Alibaba Dragonwell コミュニティでは、JDK バージョンの長期サポートを提供しています。以下のチャンネルでサポートを得たり、ディスカッションに参加したり、意見を出し合ったりすることができます。

GitHubのAlibaba DragonwellプロジェクトのIssuesページ

参考文献

[1] Oracle Java 8公式ドキュメント:https://docs.oracle.com/javase/8/
[2] OpenJDK 8 プロジェクトのホームページ: https://openjdk.java.net/projects/jdk8u/
[3]アリババDragonwell 8プロジェクト:https://github.com/alibaba/dragonwell8
[4] Alibaba Dragonwell 8 Developer Guide: https://github.com/alibaba/dragonwell8/wiki/Developer-Guide

アリババクラウドは日本に2つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ