診断ガイド

     前  次    目次     
コンテンツの開始位置

メモリ管理の概要

メモリ管理とは、新しいオブジェクトを割り当てたり、使用していないオブジェクトを削除してこれらの新しいオブジェクトに割り当てられるようにするプロセスです。この節では、まず、基本的なメモリ管理の概念を示し、Oracle JRockit JVM のオブジェクト割り当ておよびガベージ コレクションの基本について説明します。以下の事柄を説明します。

メモリ管理システムをチューニングするコマンド ライン オプションの使い方については、「メモリ管理システムのチューニング」を参照してください。

 


ヒープとナーサリ

Java オブジェクトは、ヒープと呼ばれる領域に格納されています。ヒープは、JVM が起動したときに作成され、そのサイズはアプリケーションの実行中に増減することもあります。ヒープが一杯になると、ガベージ (不要なメモリ) がコレクト (収集) されます。ガベージ コレクションは、現在使用していないオブジェクトをクリアし、新しいオブジェクトのための領域を確保します。

ただし、JVM はヒープ以外にもメモリを使用します。Java メソッド、スレッド スタック、ネイティブ ハンドルはヒープ以外のメモリに割り当てられます。JVM の内部データ構造も同様です。

ヒープは、「ナーサリ」(若い領域) と古い領域という、2 つの「領域」(世代) に分割されることもあります。ナーサリとは、新しいオブジェクトの割り当てのために予約されているヒープ内の領域のことです。ナーサリが一杯になると、若いコレクションという特別なガベージ コレクションが行われ、ナーサリに生存する古くなったオブジェクトは古い領域に昇格 (移動) されます。これにより、ナーサリが解放され、オブジェクト割り当てのための領域が確保されます。古い領域が一杯になると、古いコレクションのガベージ コレクションが行われます。これは「古いコレクション」と呼ばれます。

ナーサリの意義は、多くのオブジェクトが一時的であり、短い間しか生存しないことにあります。若いコレクションは、新しく割り当てられたオブジェクトでナーサリ内に生存しているものをすばやく検出し、ナーサリの外へ移動させます。通常、若いコレクションは、古いコレクションや一世代のガベージ コレクション (ナーサリのないヒープ) よりも格段に速く所定のメモリ領域を解放します。

R27.2.0 以降のリリースでは、ナーサリの一部は保持領域として予約されます。保持領域には、ナーサリ内に最近割り当てられたオブジェクトが保管されます。またこの領域は、次の若いコレクションが行われるときまでガベージ コレクションされません。これにより、若いコレクションの開始直前に割り当てられたオブジェクトが、そのために昇格されてしまうのを防ぐことができます。

 


オブジェクト割り当て

オブジェクトの割り当て時に、JRockit JVM は小規模オブジェクトと大規模オブジェクトを区別します。オブジェクトが大規模であると見なされるかどうかは、JVM のバージョン、ヒープ サイズ、ガベージ コレクション方式、プラットフォームなどによって決まりますが、通常の基準は 2 ~ 128 KB 程度です。詳細については、「-XXtlaSize」および「-XXlargeObjectLimit」に関するドキュメントを参照してください。

小規模オブジェクトは、スレッド ローカル領域 (TLA) に割り当てられます。ヒープから確保された空きチャンクであるスレッド ローカル領域は、Java スレッドに独占的に与えられます。この Java スレッドは、別のスレッドと同期しなくても、自分専用の TLA にオブジェクトを割り当てることができます。TLA が一杯になると、このスレッドは単に新しい TLA を要求します。ナーサリが存在する場合、TLA はナーサリから確保されますが、存在しない場合はヒープから確保されます。

TLA 内に収まらない大規模なオブジェクトは、ヒープに直接割り当てられます。ナーサリが使用されている場合、大規模なオブジェクトは古い領域に直接割り当てられます。 JRockit JVM では、同期の必要性を軽減して割り当て速度を上げるために、様々なサイズの空きチャンクによるキャッシュ システムを使用していますが、大規模なオブジェクトの割り当てには Java スレッド間の同期をより頻繁に行う必要があります。

 


ガベージ コレクション

ガベージ コレクションは、新しいオブジェクトに割り当てるために、ヒープやナーサリから領域を解放するプロセスです。この節では、JRockit JVM のガベージ コレクションについて説明します。

マーク アンド スイープ モデル

JRockit JVM は、ヒープ全体のガベージ コレクションを行う際にマーク アンド スイープ ガベージ コレクション モデルを使用します。ガベージ コレクションは、マーク フェーズとスイープ フェーズの 2 つのフェーズから成ります。

マーク フェーズでは、Java スレッド、ネイティブ ハンドルおよびその他のルート ソースから到達可能なすべてのオブジェクトは、生存状態とマークされます。同様に、これらのオブジェクトから到達可能なオブジェクトなどもマークされます。このプロセスは、現在使用されているすべてのオブジェクトを識別し、マークを付けるので、残りのオブジェクトはガベージと見なすことができます。

スイープ フェーズでは、生存しているオブジェクト間のギャップを見つけるために、ヒープがトラバースされます。見つかったギャップは、フリー リストに記録され、新しいオブジェクトの割り当てに利用されます。

JRockit JVM は、改良された 2 つのマーク アンド スイープ モデルを使用します。1 つのモデルは、コンカレント マーク アンド スイープで、もう 1 つはパラレル マーク アンド スイープです。この 2 つの方式を組み合わせて、たとえば、モーストリ コンカレント マーク アンド パラレル スイープを実行することもできます。

モーストリ コンカレント マーク アンド スイープ

モーストリ コンカレント マーク アンド スイープ方式 (通常は単に「コンカレント ガベージ コレクション」) では、ガベージ コレクションの大部分で、Java スレッドが実行され続けます。ただし、スレッドは、同期のために数回停止する必要があります。

モーストリ コンカレント マーク フェーズは、次の 4 つの部分に分割されます。

モーストリー コンカレント スイープ フェーズは、次の 4 つの部分から構成されます。

パラレル マーク アンド スイープ

パラレル マーク アンド スイープ方式 (「パラレル ガベージ コレクタ」とも呼ばれる) は、システム内の利用可能な CPU すべてを使用して、可能な限り最速でガベージ コレクションを行います。パラレル ガベージ コレクションの間を通して、すべての Java パレットは休止します。

世代別ガベージ コレクション

ナーサリが存在する場合、ナーサリで、若いコレクションという特別なガベージ コレクションが行われます。ナーサリを使用するガベージ コレクション方式は、世代別ガベージ コレクション方式、または単に世代別ガベージ コレクションとも呼ばれます。

JRockit JVM で使用される若いコレクタは、ナーサリ内の保持領域外に生存しているすべてのオブジェクトを識別し、古い領域へ昇格します。この作業は、すべての利用可能な CPU を使用して並行して行われます。若いコレクションが行われている間、Java スレッドは休止します。

動的または静的なガベージ コレクションのモード

デフォルトでは、JRockit JVM は、ガベージ コレクション方式を自動的に選択する動的なガベージ コレクションのモードを使用します。このモードは、アプリケーションのスループットを最大化することを目的としています。これ以外に、別の 2 つの動的なガベージ コレクションのモードから選択することも、静的なガベージ コレクションを選択することもできます。使用できる動的なモードは以下のとおりです。

静的な方式の主なものは以下のとおりです。

使用するアプリケーションに最適なモードまたは方式を選択する方法については、「ガベージ コレクタの選択とチューニング」を参照してください。

圧縮

隣接して割り当てられたオブジェクトが、同時に到達できなくなる (寿命が尽きる) とは限りません。このため、ガベージ コレクションの後にヒープが断片化して、ヒープ内に無数の小さな空き領域ができてしまい、大規模オブジェクトの割り当てができなくなるおそれがあります。最少スレッド ローカル領域 (TLA) のサイズよりも小さな空き領域は、一切使用できません。将来のガベージ コレクションによって、TLA に必要な大きさの領域を作成できるだけの隣接した領域が解放されるまで、ガベージ コレクタはこれらの領域をダーク マターとして廃棄し続けます。

断片化を緩和するために、JRockit JVM では、ガベージ コレクション (古いコレクション) を行うたびにヒープの部分的な圧縮を行います。圧縮とは、オブジェクトをヒープの下部に寄せ集めることにより、ヒープの最上部付近に大きな空き領域を作成する処理です。圧縮領域のサイズと位置、および圧縮方式は、使用されるガベージ コレクションのモードに応じて、高度なヒューリスティックによって選択されます。

圧縮は、スイープ フェーズの最初に行われますが、その間 Java スレッドはすべて休止します。

圧縮のチューニングの詳細については、「メモリの圧縮のチューニング」を参照してください。

外部および内部の圧縮

JRockit JVM は、外部圧縮と内部圧縮という 2 つの圧縮方式を使用します。外部圧縮では、圧縮領域内のオブジェクトを、ヒープのできるだけ下部の、圧縮領域外の空いている位置に移動 (退避) します。内部圧縮では、圧縮領域内のオブジェクトを、圧縮領域内のできるだけ下部に移動させることで近くに寄せ集めます。

JVM は、現在のガベージ コレクション モードと圧縮領域の位置に基づいて圧縮方式を選択します。外部圧縮は通常、ヒープの最上部付近で使用され、内部圧縮はオブジェクトの密度がより高いヒープの最下部付近で使用されます。

スライディング ウィンドウの概要

圧縮領域の位置は、次の位置を決めるスライディング ウィンドウをいくつか使用することによって、ガベージ コレクションごとに変わります。各スライディング ウィンドウは、ガベージ コレクションのたびに、ヒープの反対側の端に到達するか、反対方向から移動してきたスライディング ウィンドウに出会うまでヒープを 1 段階ずつ前進または後退します。このように、ヒープ全体は圧縮のたびに何度もトラバースされます。

圧縮領域のサイズ設定

圧縮領域のサイズは、使用されるガベージ コレクションのモードによって決まります。スループット モードでは、圧縮領域のサイズは静的ですが、その他のモード (静的なモードも含む) ではすべて、圧縮領域のサイズは圧縮領域の位置に合わせて調整されます。これは、実行中の最初から最後まで圧縮に要する時間を均等に保つためです。圧縮に要する時間は、移動するオブジェクトの数と、これらのオブジェクトへの参照の数によって決まります。ヒープ領域内のオブジェクトの密度が高い部分やオブジェクトへの参照の数が多い部分では、圧縮領域のサイズはより小さくなります。通常は、直近に割り当てられたオブジェクトが見つかるヒープの最上部を除いて、ヒープの上部よりも最下部付近の方がオブジェクトの密度は高くなっています。したがって、通常は、ヒープの最下部付近の方がヒープの上半分よりも圧縮領域は小さくなります。


  ページの先頭       前  次