9 Zガベージ・コレクタ

Zガベージ・コレクタ(ZGC)はスケーラブルな低レイテンシのガベージ・コレクタです。ZGCは、アプリケーション・スレッドの実行を1ミリ秒を超えて停止せずに、負荷が高いすべての作業を同時に実行します。これは、低レイテンシが必要なアプリケーションに適しています。一時休止時間は、使用しているヒープ・サイズとは無関係です。ZGCは、数百メガバイトから16TBまでのヒープ・サイズで適切に機能します。

ZGCは、適応性が高く、必要な手動構成が最小限になるように設計されています。Javaプログラムの実行中、ZGCは、生成のサイズ変更、GCスレッド数のスケーリング、および殿堂入りしきい値の調整によって、ワークロードに動的に適応します。主なチューニング・ノブは、最大ヒープ・サイズを増やすことです。

ZGCには、新しい世代別バージョンとレガシーの非世代別バージョンの2つのバージョンがあります。非世代別ZGCは古いバージョンのZGCで、世代(「世代」を参照)を利用してランタイム特性を最適化しません。ユーザーには新しい世代別ZGCを使用するように移行することをお薦めします。

世代別ZGCは、コマンド行オプション-XX:+UseZGC -XX:+ZGenerationalを使用して有効化されます。

非世代別ZGCは、コマンドライン・オプション-XX:+UseZGCを使用して有効化されます。

ヒープ・サイズの設定

ZGCの最も重要なチューニング・オプションは、-Xmxコマンドライン・オプションで設定できる最大ヒープ・サイズの設定です。ZGCはコンカレント・コレクタであるため、ヒープにアプリケーションのライブセットを格納でき、GCの実行中に割当てを提供できる十分なヘッドルームがヒープ内にあるように、最大ヒープ・サイズを選択する必要があります。必要なヘッドルームの容量は、割当て率とアプリケーションのライブセットのサイズによって大きく異なります。通常、ZGCに割り当てるメモリーが多いほどよいとされています。ただし、その一方でメモリーの浪費は望ましくないため、メモリー使用量と必要なGCの実行頻度との間でバランスが取ることが重要です。

ZGCには、-XX:SoftMaxHeapSizeという名前のヒープ・サイズに関連する別のコマンドライン・オプションがあります。これを使用すると、Javaヒープが拡大できる大きさにソフト制限を設定できます。ZGCはこの制限を超えて拡大しないように努めますが、この制限を超えて最大ヒープ・サイズまで拡大することは依然として可能です。ZGCは、Javaアプリケーションの実行速度が低下し、メモリーを再利用するためにGCを待機するのを防ぐために必要な場合にのみ、ソフト制限を超える値を使用ます。たとえば、コマンドライン・オプション-Xmx5g -XX:SoftMaxHeapSize=4gを使用すると、ZGCでは経験則による制限として4GBが使用されますが、ヒープ・サイズを4GB未満に保つことができない場合は、一時的に5GBまで使用できます。

オペレーティング・システムへの未使用メモリーの戻し

デフォルトでは、ZGCは未使用のメモリーをコミット解除し、オペレーティング・システムに戻します。これは、メモリー・フットプリントが懸念されるアプリケーションおよび環境に役立ちますが、Javaスレッドの待機時間に悪影響を及ぼす可能性があります。コマンドライン・オプション-XX:-ZUncommitを指定すると、この機能を無効化できます。さらに、ヒープ・サイズが最小ヒープ・サイズ(-Xms)を下回るように、メモリーはコミット解除されません。つまり、最小ヒープ・サイズ(-Xms)が最大ヒープ・サイズ(-Xmx)と等しくなるように構成されている場合、この機能は暗黙的に無効になります。

未コミット遅延は、-XX:ZUncommitDelay=<seconds>を使用して構成できます(デフォルトは300秒です)。この遅延は、メモリーが未コミットになるまでの未使用期間を指定します。

ノート:

アプリケーションの実行中にGCがメモリーをコミットおよびコミット解除できるようにすると、Javaスレッドの待機時間に悪影響を及ぼす可能性があります。ZGCで実行する主な理由が極めて低いレイテンシの場合は、-Xmx-Xmsに同じ値を指定して実行し、アプリケーションの起動前に-XX:+AlwaysPreTouchを使用してメモリーにページを作成することを検討してください。

大きなページの使用

大きなページを使用するようにZGCを構成すると、通常は(スループット、レイテンシおよび起動時間の観点から)パフォーマンスが向上し、設定が少し複雑になる点を除いて、実際のデメリットはありません。通常、設定プロセスにはroot権限が必要であるため、デフォルトで有効にはなりません。

Linuxでの大きなページの有効化

Linux x86では、大きなページ(huge pagesとも呼ばれる)のサイズは2MBです。

16GBのJavaヒープが必要であるとします。つまり、16GB / 2MB = 8192のhuge pagesが必要です。

ヒープには、huge pagesのプールに対して少なくとも16GB (8192ページ)のメモリーが必要です。ヒープとJVMの他の部分では、様々な内部データ構造(コード・ヒープやビットマップのマークなど)に大きなページが使用されます。この例では、9216ページ(18GB)を予約して、2GBの非Javaヒープ割当てで大きなページを使用できるようにします。

必要なページ数を持つようにシステムのhuge pageプールを構成します(root権限が必要)。

$ echo 9216 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages

カーネルがリクエストを満たす十分な空きhuge pagesを見つけられない場合、前述のコマンドの成功は保証されないことに注意してください。また、カーネルがリクエストを処理するのに時間がかかる場合があります。続行する前に、プールに割り当てられたhuge pages数をチェックし、リクエストが成功して完了したことを確認します。

$ cat /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages

9216

Linuxでの透過的huge pagesの有効化

(前述のように)明示的な大きなページを使用するかわりに、透過的huge pagesを使用することもできます。通常、透過的huge pagesの使用は、レイテンシの影響を受けやすいアプリケーションにはお薦めしません。不要なレイテンシ・スパイクが発生する傾向があるためです。ただし、ワークロードがその影響を受けるかどうか、またはどのように影響を受けるかを実験して確認する価値がある場合があります。

ノート:

Linuxでは、透過的huge pagesを有効にしたZGCを使用するには、4.7以上のカーネルが必要です。

次のオプションを使用して、VMで透過的huge pagesを有効にします。

-XX:+UseLargePages -XX:+UseTransparentHugePages

これらのオプションは、マップするメモリーに対してmadvise(..., MADV_HUGEPAGE)呼出しを発行するようにJVMに指示します。これは、madviseモードで透過的huge pagesを使用する場合に役立ちます。

透過的huge pagesを有効にするには、madviseモードを有効にしてカーネルを構成する必要もあります。

$ echo madvise > /sys/kernel/mm/transparent_hugepage/enabled

ZGCではヒープにshmem huge pagesが使用されるため、次のカーネル設定も構成する必要があります。

$ echo advise > /sys/kernel/mm/transparent_hugepage/shmem_enabled

異なるGCのパフォーマンスを比較する場合は、これらのカーネル設定を確認することが重要です。一部のLinuxディストリビューションでは、/sys/kernel/mm/transparent_hugepage/enabledalwaysに設定するように構成し、/sys/kernel/mm/transparent_hugepage/shmem_enabledをデフォルトのneverのままにして、プライベート・ページに対して透過的huge pagesを強制的に有効にします。この場合、ZGCを除くすべてのGCは、ヒープに対して透過的huge pagesを使用します。詳細は、『Transparent Hugepage Support』を参照してください。