問題は、Java 11 以降のバージョンに移行する必要 があるかどうか ではなく、 いつ行うかです。 今後数年以内に Java 8 はサポートされなくなり、ユーザーは Java 11 以降に移行する必要があります。 Java 11 への移行には利点があり、できるだけ早く行うようチームに奨励すると主張しています。
Java 8 以降、新機能が追加され、機能強化が行われました。 API に顕著な追加と変更があり、起動、パフォーマンス、メモリ使用量を向上させる拡張機能があります。
Java 11 への移行
Java 11 への移行は、段階的に行うことができます。 コードで Java モジュールを使用して Java 11 で実行する必要 はありません 。 Java 11 を使用して、JDK 8 で開発およびビルドされたコードを実行できます。 ただし、主に非推奨の API、クラス ローダー、リフレクションに関する潜在的な問題がいくつかあります。
Microsoft Java エンジニアリング グループには、 Java 8 から Java 11 に移行するためのガイドがあります。 Java プラットフォーム、Standard Edition Oracle JDK 9 移行ガイド、およびモジュール システムの状態: 互換性と移行は、他の便利なガイドです。
Java 8 と 11 の間の大まかな変更
このセクションでは、Java バージョン 9 [1]、10 [2]、および 11 [3] で行われたすべての変更を列挙するわけではありません。 パフォーマンス、診断、生産性に影響を与える変更が強調表示されています。
モジュール [4]
モジュールは、 クラスパスで実行されている大規模なアプリケーションでは管理が困難な構成とカプセル化の問題に対処します。 モジュールは、Java のクラスとインターフェイス、および関連リソースの自己記述型のコレクションです。
モジュールを使用すると、アプリケーションに必要なコンポーネントのみを含むランタイム構成をカスタマイズできます。 このカスタマイズにより、フットプリントが小さくなり、 jlink を使用してアプリケーションをデプロイ用のカスタム ランタイムに静的にリンクできます。 この小さいフットプリントは、マイクロサービス アーキテクチャで特に役立ちます。
内部的には、JVM は、クラスの読み込みをより効率的にする方法でモジュールを利用できます。 その結果、開始速度が小さく、軽量で高速なランタイムが得られます。 JVM がアプリケーションのパフォーマンスを向上させるために使用する最適化手法の方が効果的な場合があります。モジュールは、クラスで必要なコンポーネントをエンコードするためです。
プログラマの場合、モジュールは、モジュールがエクスポートするパッケージと必要なコンポーネントを明示的に宣言し、反射アクセスを制限することで、強力なカプセル化を適用するのに役立ちます。 このレベルのカプセル化により、アプリケーションのセキュリティが強化され、保守が容易になります。
アプリケーションは クラスパス を引き続き使用でき、Java 11 で実行するための前提条件としてモジュールに移行する必要はありません。
プロファイリングと診断
Java Flight Recorder [5]
Java Flight Recorder (JFR) は、実行中の Java アプリケーションから診断データとプロファイル データを収集します。 JFR は、実行中の Java アプリケーションにほとんど影響を与えはありません。 収集されたデータは、Java Mission Control (JMC) やその他のツールを使用して分析できます。 JFR と JMC は Java 8 の商用機能でしたが、どちらも Java 11 のオープン ソースです。
Java Mission Control [6]
Java Mission Control (JMC) は、Java Flight Recorder (JFR) によって収集されたデータをグラフィカルに表示し、Java 11 のオープン ソースです。JMC では、実行中のアプリケーションに関する一般的な情報に加えて、ユーザーはデータをドリルダウンできます。 JFR と JMC を使用して、メモリ リーク、GC オーバーヘッド、ホット メソッド、スレッドのボトルネック、ブロック I/O などのランタイムの問題を診断できます。
統合ログ [7]
Java 11 には、JVM のすべてのコンポーネントに共通のログシステムがあります。 この統合ログ システムを使用すると、ユーザーはログに記録するコンポーネントとレベルを定義できます。 このきめ細かいログ記録は、JVM クラッシュの根本原因分析を実行したり、運用環境でのパフォーマンスの問題を診断したりするのに役立ちます。
オーバーヘッドの少ないヒープ プロファイリング [8]
Java ヒープ割り当てをサンプリングするための新しい API が Java 仮想マシン ツール インターフェイス (JVMTI) に追加されました。 サンプリングはオーバーヘッドが少ないので、継続的に有効にすることができます。 ヒープ割り当ては Java Flight Recorder (JFR) で監視できますが、JFR のサンプリングメソッドは割り当てでのみ機能します。 JFR の実装では、割り当てが見逃される可能性もあります。 これに対し、Java 11 のヒープ サンプリングでは、ライブ オブジェクトとデッド オブジェクトの両方に関する情報を提供できます。
アプリケーション パフォーマンス監視 (APM) ベンダーは、この新機能の利用を開始しており、Java エンジニアリング グループは、Azure パフォーマンス監視ツールで潜在的な使用を調査しています。
StackWalker [9]
現在のスレッドのスタックのスナップショットの取得は、ログ記録時によく使用されます。 問題は、ログに記録するスタック トレースの量と、スタック トレースをログに記録するかどうかです。 たとえば、メソッドの特定の例外に対してのみスタック トレースを表示できます。 StackWalker クラス (Java 9 で追加) は、スタックのスナップショットを提供し、プログラマがスタック トレースを使用する方法をきめ細かく制御できるメソッドを提供します。
ガベージ コレクション [10]
Java 11 では、シリアル、並列、ガベージ ファースト、Epsilon のガベージ コレクターを使用できます。 Java 11 の既定のガベージ コレクターは、ガベージ ファースト ガベージ コレクター (G1GC) です。
ここでは、完全性のために他の 3 つのコレクターについて説明します。 Z ガベージ コレクター (ZGC) は、一時停止時間を 10 ミリ秒未満に抑えようとする、待機時間の短い同時実行コレクターです。 ZGC は、Java 11 の試験的な機能として利用できます。 Shenandoah コレクターは、実行中の Java プログラムと同時により多くのガベージ コレクションを実行することで、GC の一時停止時間を短縮する低一時停止コレクターです。 Shenandoah は Java 12 の試験的な機能ですが、Java 11 へのバックポートがあります。 コンカレント マークおよびスイープ コレクター (CMS) は使用できますが、Java 9 以降は非推奨となっています。
JVM は、平均ユース ケースに対して GC の既定値を設定します。 多くの場合、これらの既定値やその他の GC 設定は、アプリケーションの要件に従って、最適なスループットまたは待機時間を得るために調整する必要があります。 GC を適切にチューニングするには、 Microsoft Java エンジニアリング グループ が提供する GC の専門知識に関する深い知識が必要です。
G1GC
Java 11 の既定のガベージ コレクターは G1 ガベージ コレクター (G1GC) です。 G1GC の目的は、待機時間とスループットのバランスを取る方法です。 G1 ガベージ コレクターは、高い確率で一時停止時間の目標を達成することで、高スループットを実現しようとします。 G1GC は完全なコレクションを回避するように設計されていますが、同時実行コレクションでメモリを十分に高速に再利用できない場合は、フォールバックフル GC が発生します。 完全な GC では、ヤング コレクションと混合コレクションと同じ数の並列ワーカー スレッドが使用されます。
Parallel GC
並列コレクターは、Java 8 の既定のコレクターです。 Parallel GC は、複数のスレッドを使用してガベージ コレクションを高速化するスループット コレクターです。
Epsilon [11]
Epsilon ガベージ コレクターは割り当てを処理しますが、メモリを再利用しません。 ヒープが使い果たされると、JVM はシャットダウンします。 Epsilon は、有効期間の短いサービスや、ガベージ フリーであることが知られているアプリケーションに役立ちます。
Docker コンテナーの機能強化 [12]
Java 10 より前では、コンテナーに設定されたメモリ制約と CPU 制約は JVM によって認識されませんでした。 たとえば、Java 8 では、JVM の既定の最大ヒープ サイズは、基になるホストの物理メモリの 1/4 になります。 Java 10 以降では、JVM はコンテナー制御グループ (cgroup) によって設定された制約を使用して、メモリと CPU の制限を設定します (下記の注を参照)。 たとえば、既定の最大ヒープ サイズは、コンテナーのメモリ制限の 1/4 です (たとえば、-m2Gの場合は 500 MB)。
JVM オプションも追加され、Docker コンテナー ユーザーは Java ヒープに使用されるシステム メモリの量をきめ細かく制御できます。
このサポートは既定で有効になっており、Linux ベースのプラットフォームでのみ使用できます。
注
cgroup の有効化作業のほとんどは、jdk8u191 の時点で Java 8 にバックポートされました。 さらなる改善が必ずしも 8 にバックポートされるとは限りません。
マルチリリース jar ファイル [13]
Java 11 では、複数の Java リリース固有のバージョンのクラス ファイルを含む jar ファイルを作成できます。 複数リリースの jar ファイルを使用すると、ライブラリ開発者は、複数のバージョンの jar ファイルを出荷することなく、複数のバージョンの Java をサポートできます。 これらのライブラリのコンシューマーの場合、複数リリースの jar ファイルは、特定の jar ファイルを特定のランタイム ターゲットに一致させる必要がある問題を解決します。
その他のパフォーマンスの向上
JVM に対する次の変更は、パフォーマンスに直接影響します。
JEP 197: セグメント化されたコード キャッシュ [14] - コード キャッシュを個別のセグメントに分割します。 このセグメント化により、JVM メモリ占有領域の制御が向上し、コンパイルされたメソッドのスキャン時間が短縮され、コード キャッシュの断片化が大幅に減少し、パフォーマンスが向上します。
JEP 254: コンパクト文字列 [15] - 文字エンコードに応じて、文字列の内部表現を 1 文字あたり 2 バイトから 1 文字あたり 1 バイトまたは 2 バイトに変更します。 ほとんどの文字列には ISO-8859-1/Latin-1 文字が含まれるため、この変更により、文字列を格納するために必要な領域の量が実質的に半分になります。
JEP 310: アプリケーション Class-Data 共有 [16] - Class-Data 共有は、アーカイブされたクラスを実行時にメモリ マップできるようにすることで、起動時間を短縮します。 アプリケーション Class-Data 共有は、アプリケーション クラスを CDS アーカイブに配置できるようにすることで、クラス データ共有を拡張します。 複数の JVM が同じアーカイブ ファイルを共有すると、メモリが保存され、システム全体の応答時間が向上します。
JEP 312: Thread-Local ハンドシェイク [17] - グローバル VM セーフポイントを実行せずにスレッドでコールバックを実行できるようになり、グローバル セーフポイントの数を減らすことで VM の待機時間を短縮できます。
コンパイラ スレッドの遅延割り当て [18] - 階層化コンパイル モードでは、VM は多数のコンパイラ スレッドを開始します。 このモードは、多くの CPU を搭載したシステムでは既定です。 これらのスレッドは、使用可能なメモリやコンパイル要求の数に関係なく作成されます。 スレッドはアイドル状態 (ほぼ常) でもメモリを消費するため、リソースの非効率的な使用につながります。 この問題に対処するために、起動時に各型のコンパイラ スレッドを 1 つだけ開始するように実装が変更されました。 追加のスレッドを開始し、未使用のスレッドをシャットダウンすると、動的に処理されます。
コア ライブラリに対する次の変更は、新しいコードまたは変更されたコードのパフォーマンスに影響します。
JEP 193: 変数ハンドル [19] - オブジェクト フィールドと配列要素に対してさまざまな java.util.concurrent.atomic および sun.misc.Unsafe 操作と同等の操作を呼び出す標準的な手段、メモリ順序をきめ細かく制御するための標準的なフェンス操作、および参照先オブジェクトが確実に強く到達可能であることを保証する標準的な到達可能性フェンス操作を定義します。
JEP 269: コレクションの便利なファクトリ メソッド [20] - 少数の要素でコレクションとマップのインスタンスを作成するのに便利なライブラリ API を定義します。 コンパクトで変更不可能なコレクション インスタンスを作成するコレクション インターフェイス上の静的ファクトリ メソッド。 これらのインスタンスは本質的により効率的です。 API は、コンパクトに表され、ラッパー クラスを持たないコレクションを作成します。
JEP 285: Spin-Wait ヒント [21] - Java がランタイム システムにスピン ループ内であることをヒントする API を提供します。 特定のハードウェア プラットフォームは、スレッドがビジーウェイト状態にあることをソフトウェアで示すことで恩恵を受けます。
JEP 321: HTTP クライアント (Standard) [22]- HTTP/2 と WebSocket を実装し、従来の HttpURLConnection API を置き換えることができる新しい HTTP クライアント API を提供します。
References
[1] Oracle Corporation、"Java Development Kit 9 リリース ノート"、(オンライン)。 使用可能: https://www.oracle.com/technetwork/java/javase/9u-relnotes-3704429.html。 (2019 年 11 月 13 日アクセス)。
[2] Oracle Corporation、"Java Development Kit 10 リリース ノート"(オンライン)。 使用可能: https://www.oracle.com/technetwork/java/javase/10u-relnotes-4108739.html。 (2019 年 11 月 13 日アクセス)。
[3] Oracle Corporation、"Java Development Kit 11 リリース ノート"、(オンライン)。 使用可能: https://www.oracle.com/technetwork/java/javase/11u-relnotes-5093844.html。 (2019 年 11 月 13 日アクセス)。
Oracle Corporation、「Project Jigsaw」、2017年9月22日。(オンライン) 使用可能: http://openjdk.java.net/projects/jigsaw/。 (2019 年 11 月 13 日アクセス)。
2018年9月9日、Oracle Corporation「JEP 328: Flight Recorder」 (オンライン)。 使用可能: http://openjdk.java.net/jeps/328。 (2019 年 11 月 13 日アクセス)。
オラクルコーポレーション「ミッションコントロール」2019年4月25日 (オンライン)。 使用可能: https://wiki.openjdk.java.net/display/jmc/Main。 (2019 年 11 月 13 日アクセス)。
[7] Oracle Corporation、"JEP 158: Unified JVM Logging"、2019 年 2 月 14 日。 (オンライン)。 使用可能: http://openjdk.java.net/jeps/158。 (2019 年 11 月 13 日アクセス)。
[8] Oracle Corporation「JEP 331: 低オーバーヘッドのヒーププロファイリング」2018年9月5日。 (オンライン)。 使用可能: http://openjdk.java.net/jeps/331。 (2019 年 11 月 13 日アクセス)。
[9] Oracle Corporation、"JEP 259: Stack-Walking API"、2017 年 7 月 18 日。 (オンライン)。 使用可能: http://openjdk.java.net/jeps/259。 (2019 年 11 月 13 日アクセス)。
2017 年 9 月 12 日、Oracle Corporation、「JEP 248: G1 を既定のガベージコレクターにする」 (オンライン)。 使用可能: http://openjdk.java.net/jeps/248。 (2019 年 11 月 13 日アクセス)。
Oracle Corporation「JEP 318: Epsilon: a No-Op ガベージ コレクター」、2018年9月24日。 (オンライン)。 使用可能: http://openjdk.java.net/jeps/318。 (2019 年 11 月 13 日アクセス)。
[12] Oracle Corporation、"JDK-8146115: Docker コンテナーの検出とリソース構成の使用の向上"、2019 年 9 月 16 日。 (オンライン)。 使用可能: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8146115。 (2019 年 11 月 13 日アクセス)。
2017 年 6 月 22 日、Oracle Corporation、 "JEP 238: Multi-Release JAR Files". (オンライン)。 使用可能: http://openjdk.java.net/jeps/238。 (2019 年 11 月 13 日アクセス)。
[14] Oracle Corporation、"JEP 197: Segmented Code Cache"、2017 年 4 月 28 日。 (オンライン)。 使用可能: http://openjdk.java.net/jeps/197。 (2019 年 11 月 13 日アクセス)。
Oracle Corporation:「JEP 254: Compact Strings」(2019年5月18日) (オンライン)。 使用可能: http://openjdk.java.net/jeps/254。 (2019 年 11 月 13 日アクセス)。
[16] Oracle Corporation、"JEP 310: Application Class-Data Sharing"、2018 年 8 月 17 日。 (オンライン)。 使用可能: https://openjdk.java.net/jeps/310。 (2019 年 11 月 13 日アクセス)。
2019年8月21日、Oracle Corporation「JEP 312: Thread-Local ハンドシェイク」。 (オンライン)。 使用可能: https://openjdk.java.net/jeps/312。 (2019 年 11 月 13 日アクセス)。
[18] Oracle Corporation、"JDK-8198756: コンパイラ スレッドの遅延割り当て"、2018 年 10 月 29 日。 (オンライン)。 使用可能: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8198756。 (2019 年 11 月 13 日アクセス)。
[19] Oracle Corporation、"JEP 193: 変数ハンドル"、2017 年 8 月 17 日。 (オンライン)。 使用可能: https://openjdk.java.net/jeps/193。 (2019 年 11 月 13 日アクセス)。
[20] Oracle Corporation、「JEP 269: Convenience Factory Methods for Collections」、2017年6月26日。 (オンライン)。 使用可能: https://openjdk.java.net/jeps/269。 (2019 年 11 月 13 日アクセス)。
2017年8月20日、Oracle Corporation「JEP 285: Spin-Wait ヒント」。 (オンライン)。 使用可能: https://openjdk.java.net/jeps/285。 (2019 年 11 月 13 日アクセス)。
[22] Oracle Corporation、"JEP 321: HTTP クライアント (Standard)"、2018 年 9 月 27 日。 (オンライン)。 使用可能: https://openjdk.java.net/jeps/321。 (2019 年 11 月 13 日アクセス)。