Java Platform, Standard Edition HotSpot Virtual Machineガベージ・コレクション・チューニング・ガイド
目次      

3 世代

Java SEプラットフォームの強みの1つは、メモリー割当てとガベージ・コレクションの複雑さが開発者から隠蔽されることです。ただし、ガベージ・コレクションが重要なボトルネックになった場合、この目に見えない実装面を理解しておくと役に立ちます。ガベージ・コレクタでは、アプリケーションがどのようにオブジェクトを使用するかについて前提を立て、それを調整可能なパラメータに反映して、これらのパラメータを調整することで、抽象化の威力を犠牲にすることなくパフォーマンスを改善できます。

実行中のプログラムでどのポインタからもアクセスされなくなると、オブジェクトはガベージ(廃棄物)とみなされます。最も簡単なガベージ・コレクション・アルゴリズムでは、到達可能なすべてのオブジェクトを1つずつ調べます。残ったオブジェクトがガベージとみなされます。この方法ではライブ・オブジェクトの数に比例した時間がかかるので、大量のライブ・データが保持される大規模なアプリケーションでは使用できません。

仮想マシンには様々な種類のガベージ・コレクション・アルゴリズムが組み込まれており、それらは世代別コレクションを使用して組み合されます。ネイティブ・ガベージ・コレクションではヒープ内のすべてのライブ・オブジェクトを調べますが、世代別コレクションでは、ほとんどのアプリケーションで経験的に得られたいくつかの特徴を利用して、未使用(ガベージ)オブジェクトの回収に必要な作業を最小限に抑えます。このように得られた特徴の中で最も重要なのは弱い世代別仮説であり、この説では、ほとんどのオブジェクトは短命であることが示されています。

図3-1「オブジェクトの典型的な寿命分布」に青で示した領域が、オブジェクトの典型的な寿命分布です。X軸は、オブジェクトの寿命を、割り当てられたバイト数で示しています。Y軸のバイト数は、その寿命を持つオブジェクト群の総バイト数です。左端の鋭いピークは、割当て後にすぐに回収できる(「寿命を終えた」)オブジェクトを表しています。たとえば、Iteratorオブジェクトは、1つのループ期間だけ生存するのが一般的です。

図3-1 オブジェクトの典型的な寿命分布

図3-1の説明が続きます
「図3-1 オブジェクトの典型的な寿命分布」の説明

より寿命が長いオブジェクトもあるので、分布は右側に伸びています。たとえば、初期化時に割り当てられ、プロセスが終了するまで生き続けるオブジェクトはよくあります。この2種類の両極端なオブジェクトの中間に、なんらかの中間演算の間だけ生存するオブジェクトがあり、ここでは初期ピークの右側のこぶとして現れています。これとはまったく違う分布になるアプリケーションもありますが、驚くほど多くのアプリケーションで、この一般的な形状が当てはまります。オブジェクトの大半が「短命」であるという事実に着目すると、コレクションの効率化が可能になります。

このシナリオを最大限に活用するのが、メモリーの世代(年齢ごとにオブジェクトを保持するメモリー・プール)別管理です。ある世代がいっぱいになったときに、その世代でガベージ・コレクションを実行します。大半のオブジェクトは若いオブジェクト用のプール(若い世代)に割り当てられ、ほとんどのオブジェクトはそこで寿命を終えます。若い世代がいっぱいになると、マイナー・コレクションが発生して、若い世代のみが収集されます。他の世代のガベージは回収されません。マイナー・コレクションは、弱い世代別仮説が有効で、若い世代のオブジェクトのほとんどがガベージであり回収できるという前提に基づくことによって、最大限に活用できます。このようなコレクションのコストは、一次的には、収集されるライブ・オブジェクトの数に比例します。寿命を終えたオブジェクトでいっぱいの若い世は非常にすばやく収集されます。一般に、各マイナー・コレクションで生き残ったオブジェクトの一部は若い世代からTenured世代に移されます。最終的にTenured世代がいっぱいになって収集が必要になると、メジャー・コレクションが発生し、ヒープ全体が収集されます。メジャー・コレクションは、非常に多くのオブジェクトを対象とするため、通常はマイナー・コレクションよりはるかに長い時間がかかります。

「エルゴノミクス」の項で説明したように、エルゴノミクスはガベージ・コレクタを動的に選択して、様々なアプリケーションのパフォーマンスを改善します。シリアル・ガベージ・コレクタは小規模なデータ・セットを保持するアプリケーション向けに設計され、そのデフォルト・パラメータは大部分の小規模アプリケーションで効率的になるよう選択されています。パラレル・ガベージ・コレクタやスループット・ガベージ・コレクタは、中規模から大規模なデータ・セットを保持するアプリケーションでの使用を想定しています。エルゴノミクスで選択されるヒープ・サイズのパラメータと適応型サイズ・ポリシーの機能は、サーバー・アプリケーションのパフォーマンスを改善することを目的としています。これらの選択は適切に機能することが多いですが、すべてのケースでそうとはかぎらないので、このドキュメントでは、その点を中心に説明します。


注:

ガベージ・コレクションがボトルネックになった場合は、合計ヒープ・サイズだけでなく個々の世代のサイズをカスタマイズすることが高い確率で必要になります。冗長ガベージ・コレクタ出力を確認してから、ガベージ・コレクタのパラメータに対する各パラメータ・メトリックの感度を調べてください。

図3-2「世代のデフォルトの配置(パラレル・コレクタとG1を除く)」に、各世代のデフォルトの配置を示します(パラレル・コレクタとG1を除くすべてのコレクタが対象)。

図3-2 世代のデフォルトの配置(パラレル・コレクタとG1を除く)

図3-2の説明が続きます
「図3-2 世代のデフォルトの配置(パラレル・コレクタとG1を除く)」の説明

初期化時には、最大のアドレス空間が仮想的に確保されますが、必要になるまでは物理メモリーは割り当てられません。オブジェクト・メモリー用に確保されるアドレス空間全体は、若い世代とTenured世代に分けることができます。

若い世代はEden領域と2つのSurvivor領域で構成されています。ほとんどのオブジェクトは最初にEdenに割り当てられます。Survivor領域の一方は常に空で、Eden内のライブ・オブジェクトのコピー先として使用されます。もう一方のSurvivor領域は次回のコピー方式コレクションのコピー先です。このようにして、Survivor領域間でオブジェクトがコピーされ、そのオブジェクトが十分な年齢に達すると、Tenured世代へコピーされます。

パフォーマンスに関する考慮事項

ガベージ・コレクションのパフォーマンスを測定するには、2つの主要な方法があります。

  • スループットは、長期間で見たときの、ガベージ・コレクション以外の処理に使用される時間の割合です。スループットには割当てにかかる時間も含まれます(ただし、通常は割当ての速度を調整する必要はありません)。

  • 一時停止は、ガベージ・コレクションが実行中のために、アプリケーションが応答しないように見える時間です。

ガベージ・コレクションの要件は、ユーザーによって様々です。たとえば、ガベージ・コレクション中の一時停止は許容できるか、ネットワーク遅延の前では目立たないため、Webサーバーの適切なメトリックはスループットだと考えるユーザーもいます。これに対して、対話型のグラフィック・プログラムでは、ほんの短い一時停止でも、ユーザーの操作性を損う可能性があります。

これ以外のことを重視するユーザーもいます。フットプリントは、プロセスの作業セットをページ数やキャッシュ・ライン数で測定したものです。物理メモリーが制限されていたり、プロセス数が多いシステムでは、フットプリントがスケーラビリティを左右する可能性があります。機敏性は、オブジェクトが寿命を終えてから、そのメモリーが使用可能になるまでの時間で、Remote Method Invocation (RMI)などの分散システムでは、重要な考慮事項になります。

一般に、特定の世代のサイズを選択することは、これらの考慮事項とのトレードオフです。たとえば、若い世代を非常に大きくすると、スループットが最大になりますが、フットプリント、機敏性および一時停止時間が犠牲になります。若い世代を小さくすると、若い世代の一時停止を最小にできますが、スループットが犠牲になります。1つの世代のサイズを変更しても、別の世代のコレクションの頻度や一時停止時間には影響しません。

世代のサイズを選択する方法は1つではありません。アプリケーションによるメモリーの使用方法とユーザー要件によって、最適な選択が決まります。そのため、仮想マシンによるガベージ・コレクタの選択が必ずしも最適とはかぎらないので、コマンド行オプションを使用してオーバーライドできます(「世代のサイズ設定」を参照)。

測定

スループットとフットプリントの最適な測定方法は、アプリケーション専用のメトリックスを使用するやり方です。たとえば、Webサーバーのスループットはクライアント負荷生成プログラムを使用してテストでき、そのサーバーのフットプリントは、pmapコマンド(Solarisオペレーティング・システムの場合)を使用して測定できます。これに対して、ガベージ・コレクションによる一時停止は、仮想マシン自体の診断出力をチェックすることで簡単に推測できます。

コマンド行オプション-verbose:gcを使用すると、コレクションごとにヒープとガベージ・コレクションに関する情報が出力されます。次の例は、大規模なサーバー・アプリケーションの出力を示しています。

[GC 325407K->83000K(776768K), 0.2300771 secs]
[GC 325816K->83372K(776768K), 0.2454258 secs]
[Full GC 267628K->83769K(776768K), 1.8479984 secs]

この出力から、2回のマイナー・コレクションの後に1回のメジャー・コレクションが発生していることがわかります。矢印の前後の数値は(たとえば、1行目の325407K->83000K)は、ガベージ・コレクションの前後のライブ・オブジェクトの合計サイズをそれぞれ示しています。マイナー・コレクション後のサイズには、ガベージ(生存していない)にもかかわらず、回収できないオブジェクトが含まれています。これらのオブジェクトはTenured世代に含まれているか、Tenured世代から参照されています。

次のカッコ内の数値(たとえば、1行目の(776768K))は、コミットされたヒープ・サイズ、つまりオペレーティング・システムから追加のメモリーを要求せずに、Javaオブジェクトに使用可能な領域量です。この数値に含まれるのは、一方のSurvivor領域のみです。ガベージ・コレクション時以外は、常に一方のSurvivor領域のみを使用して、オブジェクトを格納します。

行の最後の項目(たとえば、0.2300771 secs)は、コレクションの実行に要した時間を示しています(この例では、約1/4秒です)。

3行目のメジャー・コレクションも形式は同じです。


注:

-verbose:gcによって生成される出力形式は、今後のリリースで変更されることがあります。

コマンド行オプション-XX:+PrintGCDetailsを使用すると、コレクションに関する追加情報が出力されます。ここでは、シリアル・ガベージ・コレクタを使用した場合の-XX:+PrintGCDetailsの出力例を示します。

[GC [DefNew: 64575K->959K(64576K), 0.0457646 secs] 196016K->133633K(261184K), 0.0459067 secs]

これは、マイナー・コレクションで若い世代の約98%がリカバリされ(DefNew: 64575K->959K(64576K))、所要時間が0.0457646 secs (約45ミリ秒)であったことを示しています。

ヒープ全体の使用率は約51% (196016K->133633K(261184K))に低下し、最後に表示された時間0.0459067 secsから、コレクションのオーバーヘッドが(若い世代のコレクションに加えて)若干増加していることがわかります。


注:

-XX:+PrintGCDetailsによって生成される出力形式は、今後のリリースで変更されることがあります。

オプション-XX:+PrintGCTimeStampsは、各コレクションの開始時にタイム・スタンプを追加します。これによりガベージ・コレクションの発生頻度がわかるので便利です。

111.042: [GC 111.042: [DefNew: 8128K->8128K(8128K), 0.0000505 secs]111.042: [Tenured: 18154K->2311K(24576K), 0.1290354 secs] 26282K->2311K(32704K), 0.1293306 secs]

コレクションは、アプリケーションの実行後約111秒後に開始されています。マイナー・コレクションが、ほぼ同じ時間に開始されています。また、メジャー・コレクションに関する情報が、Tenuredを使用して示されています。Tenured世代の使用率が約10% (18154K->2311K(24576K))に低下し、所要時間は0.1290354 secs (約130ミリ秒)でした。

目次      

Copyright © 1993, 2020, Oracle and/or its affiliates. All rights reserved.