Jump to content 日本-日本語

製品  >  ソフトウェア  >  HP-UX   >  Knowledge-on-Demand  >  Javaパフォーマンス・チューニング

Javaパフォーマンス・チューニング

第3回:ヒープ・メモリ管理

HP-UX/Integrityサーバー お問い合せ
コンテンツに進む
Javaパフォーマンス・チューニング:第3回 Javaパフォーマンス・チューニング:第3回
今回は、Javaにおけるヒープ・メモリ管理の詳細を説明します。JVMのヒープ・メモリの中で、新しいオブジェクトと古いオブジェクトがどのように配置されるかを理解することで、ヒープ・メモリが有効に利用されているか否かを判断することができます。また、JVMが出力するガベージ・コレクションのログを解析し、オプションの指定によってヒープ・メモリのサイズを適切にチューニングする方法を紹介します。
Javaパフォーマンス・チューニング 第3回
Javaにおけるヒープ・メモリ管理
チューニング結果の解析
2004年5月
ページ: 1   2   |   次へ 

Java ヒープ・メモリの構造


Javaにおけるガベージ・コレクションのメカニズムを理解するには、まずヒープ・メモリの構造を知っておく必要があります。

図1:JVMヒープ・メモリの構造
図1:JVMヒープ・メモリの構造

図1は、JVM におけるヒープ・メモリの構造を示したものです。この図が示すように、ヒープ・メモリの中には新しいオブジェクトを格納する「NEW」領域と、古いオブジェクトを格納する「OLD」領域が存在します。生まれてすぐに不要となる短命なオブジェクトはNEW領域でその一生を過ごし、長時間存在するオブジェクトはOLD領域に留まることになります。また、図1には示されていませんが、ヒープ・メモリにはJVMにロードされたクラスの置き場所として利用される「Permanent」領域も存在します。

JVMでは、「Scavenge GC」と「Full GC」という2種類のガベージ・コレクションが実行されます。Scavenge GCはNEW領域のみを対象とした短時間で終了するガベージ・コレクションであり、頻繁に実施されます。一方、Full GCはNEWとOLD両方の領域を対象とした大がかりなガベージ・コレクションであり、比較的低い頻度で実施されます。こうした理由から、ヒープ・メモリ全体がオブジェクトの世代別に分割されています。

JVMのデフォルト設定では、NEW領域の起動時のサイズが2MB、最大サイズが16MBにセットされています。また、OLD領域の起動時のサイズは4MB、最大サイズは48MBです。これらのサイズは、以下のJVM オプションを用いることで変更できます。

オプション名

内容

-Xms ヒープ・メモリ全体の起動時のサイズ
-Xmx ヒープ・メモリ全体の最大サイズ
-Xmn NEW領域のサイズ
-XX:SurvivorRatio=<n> Eden領域のサイズをFromまたはTo領域のサイズで割った値(FromとTo領域は同じサイズ)

ここで、最初の3種類のオプションについては、指定するサイズをメガバイト単位で指定します。例えば「-Xmn256m」は、NEW領域に256MBを割り当てることを意味します。これらのオプションの使用方法については、以下に詳しく説明します。

世代別ガベージ・コレクションのメカニズム


図1に示されるとおり、NEW領域内はさらに「Eden」「From」「To」という3つの領域に分割されています。これらのうち、Eden領域は、新しいオブジェクトが作成された際に最初に配置されるメモリ領域です。よって同領域は、時間が経つとともに新しいオブジェクトで埋め尽くされていきます。

Eden領域が満杯になると、前述したScavenge GCが実行されます。このとき、まだ使用中のオブジェクト(すなわち、他のオブジェクトから参照されているオブジェクト)についてはEden領域からTo領域へと移動され、参照されていない不要なオブジェクトは破棄されます。これにより、Eden領域全体がクリーンアップされ、再び新しいオブジェクトを受け入れ可能になります(図2)。

図2:Scavenge GCによるEden領域からTo領域へのオブジェクト移動
図2:Scavenge GCによるEden領域からTo領域へのオブジェクト移動

ここで、図2のTo領域に移動されたオブジェクトに「1」という数値が振られていることに注目してください。これはScavenge GCによってオブジェクトが移動した回数を表しています。この数値の意味については追って説明します。

続いて、Eden領域が再度満杯になり、2回目のScavenge GCが実行される状況を考えます。このとき、前回のGC時のFrom領域とTo領域は互いに入れ替わることになります。つまり、前回のGCでオブジェクトの移動先とされたTo領域は、次回のGCではFrom領域として扱われるわけです。そして、このFrom領域とEden領域にある使用中のオブジェクトが、再びTo領域に移動されます(図3)。

図3:2回目のScavenge GCによるオブジェクト移動
図3:2回目のScavenge GCによるオブジェクト移動

このように、Scavenge GCでは、オブジェクトが不要になるまでの間、From領域とTo領域の間でオブジェクトの移動を繰り返します。そして、この移動の際には、オブジェクトに振られた移動回数の数値がカウントアップされます。図3を見れば、From領域からTo領域へ移動したオブジェクトの移動回数が「2」に増えていることお分かりいただけるでしょう。Scavenge GCでは、この移動回数が「MaxTenuringThreshold」と呼ばれるしきい値を上回るオブジェクトについて、OLD領域への移動を行います。

JVMにおけるMaxTenuringThresholdのデフォルト値は32に設定されています。よって新しいオブジェクトは、最大で32回までScavenge GCの対象となり、その間をFrom領域とTo領域の中で過ごします。この回数を超えて生き延びたオブジェクトは、「寿命の長いオブジェクト(tenured object)」としてOLD領域に移動される仕組みです。

Scavenge GCのチューニング


ここまでの説明によって、前回説明したオプション-Xverbosegcによるガベージ・コレクション・ログの内容をより理解できるはずです。例えば、ログ中の以下のような出力を考えてみます。

eden: 1834928->0/3670016

このログは、以下のように解釈します。

  • GC前、Eden領域の消費サイズは「1834928」バイトであった
  • GC後、Eden領域の消費サイズは「0」バイトであった(つまり全オブジェクトが移動もしくは破棄された)
  • GC後、Eden領域のサイズは「3670016」バイトであった
From領域およびTo領域についても、これと同様の方法でログの解釈が可能です。

survivor: 120576->0/262144

「survivor」とは、From領域とTo領域両方を指します。ここでもし、上記ログのようにGC後のFrom/To領域の消費サイズが「0」となった場合は注意が必要です。これはすなわち、オブジェクトがFrom領域とTo領域の間を行き来せず、すぐにOLD領域に移動してしまっていることを表します。このような状況では、OLD領域は短命なオブジェクトですぐに埋まり、Full GCが頻発してしまいます。これはオーバーフローと呼ばれ、MaxTenuringThreshold値の低い状態で一連のGCが発生している状況を見つけることで検出できます。

NEW領域のサイズ調節


MaxTenuringThreshholdの値は、ガベージ・コレクションが進行するに従い調整されるため、プログラム実行中は常に変化する可能性があります。NEW領域が小さすぎ、オブジェクトを短期間しか保持できない状況では、MaxTenuringThreshholdの値が低下しオブジェクトはNEW領域からOLD領域へ過度に移動しやすくなります。これは避けるべき状況です。そこで、MaxTenuringThreshholdを可能なかぎり32に近づけ、Full GCの回数を減らすことを目的として、以下のようなチューニングを実施します。

NEW領域を拡大するには、-Xmnオプションを用いて同領域のサイズを指定します。これにより、NEW領域内の全ての領域(Eden、From、To)が拡張されます。同オプションは、以下のように使用します。

$ java -Xmn160m -Xmx480m -Xms480m <クラス名>

最大ヒープ・サイズ(-Xmxで指定)に対するNEW領域の割合は、1/3から1/2が推奨される値です。NEW領域をより積極的に拡張したい場合は、1/2程度のサイズを指定します。
また、From領域とTo領域のサイズは、Eden領域のサイズに対する比率を指定して設定できます。具体的には、オプション-XX:SurvivorRatioを以下のように使用します。

$ java -Xmn120m -Xmx480m -Xms480m -XX:SurvivorRatio=8 <クラス名>

これにより、Eden領域 のサイズはFrom/To領域のサイズの8倍となります(From領域とTo領域のサイズは同じです)。上記の例の場合、NEW領域は96MB(8×12MB)のEden領域と、それぞれ12MBのFrom/To領域に分割され、これらの合計は120MBとなります。ちなみに、-XX:SurvivorRatioの値はデフォルトのままにしておくことが推奨されています。この値はJDK 1.3.1 では8に設定されています。

連載記事一覧 ページ: 1   2   |   次へ  次のページへ

本ページの内容は執筆時の情報に基づいており、異なる場合があります。

お問い合わせ

ご購入前のお問い合わせ


ご購入後のお問い合わせ

HPEサポートセンター
製品の標準保証でご利用いただける無償のサービスです。

ショールーム

ショールーム 導入をご検討のお客様へ
業務アプリケーションの継続・標準化・開発性とシステム担当者様、システム開発者様が抱える悩み・疑問に対する解決策実体験して頂けます。
印刷用画面へ
プライバシー ご利用条件・免責事項