1. 序章

ガベージコレクター(GC)は、Javaでのメモリ管理を処理します。 その結果、プログラマーはメモリの割り当てと割り当て解除を明示的に処理する必要がありません。

Javaでは、JVMは最初に一定量のメモリを予約します。 実際に使用されるメモリが予約量より大幅に少ない場合があります。 このようなシナリオでは、余分なメモリをOSに戻すことをお勧めします。

このプロセス全体は、ガベージコレクションに使用されるアルゴリズムに依存しています。 したがって、必要な動作に従って、GCとJVMのタイプを選択できます。

このチュートリアルでは、GCによるメモリ管理とOSとの相互作用について説明します。

2. JVMメモリ構成

JVMが初期化されると、ヒープ領域、スタック領域、メソッド領域、PCレジスタ、ネイティブメソッドスタックなど、さまざまなタイプのメモリ領域がその中に作成されます。

GCはヒープストレージを扱います。 したがって、この記事では、ヒープに関連するメモリの相互作用に焦点を当てます。

flags -Xmsおよび-Xmxをそれぞれ使用して、初期ヒープサイズと最大ヒープサイズを指定できます。 -Xmsが-Xmxよりも小さい場合は、JVMが最初にすべての予約済みメモリをヒープにコミットしていないことを意味します。 つまり、ヒープサイズは-Xmsから始まり、-Xmxまで拡張できます。 これにより、開発者は必要なヒープメモリのサイズを構成できます。

これで、アプリケーションの実行時に、さまざまなオブジェクトにヒープ内のメモリが割り当てられます。 ガベージコレクション時に、GCは参照されていないオブジェクトの割り当てを解除し、メモリを解放します。 この割り当て解除されたメモリは、各割り当て解除後にOSと対話するためのCPUを集中的に使用する手順であるため、現在はヒープ自体の一部です。

オブジェクトは、ヒープ内に分散して存在します。 GCは、メモリを圧縮し、OSに戻るための空きブロックを作成する必要があります。 これには、メモリを返す際の追加のプロセス実行が含まれます。 また、Javaアプリケーションは、後の段階で追加のメモリを必要とする場合があります。 このため、 OSと再度通信して、より多くのメモリを要求する必要があります。 さらに、要求された時間でOSのメモリの可用性を保証できません。 したがって、メモリをフェッチするためにOSを頻繁に呼び出すのではなく、内部ヒープを使用する方が安全なアプローチです。

ただし、アプリケーションがヒープメモリ全体を必要としない場合は、OSが他のアプリケーションに使用している可能性のある使用可能なリソースをブロックしているだけです。 これを考慮して、JVMはメモリ解放のための効率的で自動化された手法を導入しました。

3. ガベージコレクター

さまざまなリリースバージョンを経て、JavaはさまざまなタイプのGCを導入しました。 ヒープとOS間のメモリの相互作用は、JVMとGCの実装に依存します。 一部のGC実装は、ヒープの縮小を積極的にサポートしています。 ヒープの縮小は、リソースを最適に使用するために、余分なメモリをヒープからOSに解放するプロセスです。

たとえば、ParallelGCは未使用のメモリをOSにすぐに解放しません。 一方で、一部 GCはメモリ消費量を分析し、それに応じてヒープから空きメモリを解放するかどうかを決定します。 G1、シリアル、 シェナンドア、およびZ GC ヒープの縮小をサポートします。

これらのプロセスを見てみましょう。

3.1. ガベージファースト(G1)GC

G1は、Java9以降のデフォルトのGCです。 長い休止なしで圧縮プロセスをサポートします。 内部適応最適化アルゴリズムを使用して、アプリケーションの使用状況に応じて必要なRAMを分析し、必要に応じてメモリのコミットを解除します

初期実装は、完全なGCの後、または同時サイクルイベント中のヒープ縮小をサポートします。 ただし、理想的な状況では、特にアプリケーションがアイドル状態のときに、未使用のメモリをOSにすぐに戻したいと考えています。 GCが、実行時にアプリケーションによるメモリ使用量に動的に適応するようにします。

Javaには、さまざまなGCにそのような機能が含まれています。 G1の場合、 JEP346でこれらの変更が導入されています。 Java 12以降では、同時リマークフェーズでヒープ縮小も可能です。 G1は、アプリケーションがアイドル状態のときにヒープの使用状況を分析しようとし、必要に応じて定期的なガベージコレクションをトリガーします。 G1は、 G1PeriodicGCInvokesConcurrent オプションに基づいて、同時サイクルまたは完全なGCを開始できます。 サイクルの実行後、G1はヒープのサイズを変更し、解放されたメモリをOSに戻す必要があります。

3.2. シリアルGC

シリアルGCは、ヒープ縮小動作もサポートしています。 G1と比較すると、解放されたメモリのコミットを解除するには、さらに4回の完全なGCサイクルが必要です。

3.3. ZGC

ZGCはJava11で導入されました。 また、 JEP351のOSに未使用のメモリを戻す機能も追加されました。

3.4. シェナンドアGC

ShenandoahはコンカレントGCです。 ガベージコレクションを非同期的に実行します。 完全なGCの必要性を排除することは、アプリケーションのパフォーマンスの最適化に大いに役立ちます。

4. JVMフラグの使用

これまで、JVMコマンドラインオプションを使用してヒープサイズを指定できることを確認しました。 同様に、さまざまなフラグを使用して、GCのデフォルトのヒープ縮小動作を構成できます。

  • -XX:GCTimeRatio :アプリケーションの実行とGCの実行の間で必要な時間分割を指定します。 これを使用して、GCの実行時間を長くすることができます
  • -XX:MinHeapFreeRatio :ガベージコレクション後のヒープ内の空き領域の最小予想比率を指定します
  • -XX:MaxHeapFreeRatio :ガベージコレクション後のヒープ内の空き領域の予想される最大比率を指定します

ヒープ内の使用可能な空き領域が、-XX:MaxHeapFreeRatioオプションを使用して指定された比率よりも大きい場合、GCは未使用のメモリをOSに戻すことができます。 上記のフラグの値を構成して、ヒープ内の未使用のメモリの量を制限できます。 同時ガベージコレクションプロセスで利用できる同様のオプションがあります。

  • -XX:InitiatingHeapOccupancyPercent :同時ガベージコレクションを開始するために必要なヒープ占有率を指定します。
  • -XX:-ShrinkHeapInSteps :ヒープサイズを -XX:MaxHeapFreeRatio値にすぐに縮小します。 デフォルトの実装では、このプロセスに複数のガベージコレクションサイクルが必要です。

5. 結論

この記事では、Javaがさまざまな要件に対応するさまざまなタイプのGCを提供することを確認しました。 GCは、空きメモリを再利用してホストOSに戻すことができます。 要件に応じてGCのタイプを選択できます。

また、JVMパラメーターを使用してGCの動作を微調整し、目的のパフォーマンスレベルに到達することも検討しました。 さらに、JVMによるメモリ使用率の動的スケーリングを選択できます。 アプリケーションと関連するリソースに対して選択した各オプションに関連するトレードオフを検討する必要があります。