JProfiler ヘルプ

ヒープウォーカー

ヒープスナップショット

オブジェクト間の参照を含むヒープ解析にはヒープスナップショットが必要です。なぜなら、JVMに対してオブジェクトへの参照元を直接問い合わせることはできないためです。そのため、その問いに答えるにはヒープ全体をイテレートする必要があります。ヒープスナップショットから、JProfilerはヒープウォーカーの各ビューに必要なデータを効率的に生成するための内部データベースを作成します。

ヒープスナップショットには2つのソースがあります:JProfilerヒープスナップショットとHPROF/PHDヒープスナップショットです。JProfilerヒープスナップショットはヒープウォーカーの全ての機能をサポートしています。プロファイリングエージェントはJVMTIプロファイリングインターフェースを使って全ての参照をイテレートします。プロファイルされたJVMが別のマシン上で動作している場合、全ての情報はローカルマシンに転送され、以降の計算はローカルで行われます。HPROF/PHDスナップショットはJVMの組み込みメカニズムで作成され、JProfilerが読み込める標準フォーマットでディスクに書き込まれます。HotSpot JVMはHPROFスナップショットを作成でき、Eclipse OpenJ9 JVMはPHDスナップショットを提供します。

ヒープウォーカーの概要ページでは、JProfilerヒープスナップショットかHPROF/PHDヒープスナップショットのいずれかを作成するか選択できます。デフォルトではJProfilerヒープスナップショットが推奨されます。HPROF/PHDヒープスナップショットは、別の章で説明されている特別な状況で有用です。

選択ステップ

ヒープウォーカーは、選択されたオブジェクトセットの異なる側面を表示する複数のビューで構成されています。ヒープスナップショットを取得した直後は、ヒープ上の全てのオブジェクトが表示されます。各ビューには、選択したオブジェクトを現在のオブジェクトセットにするためのナビゲーションアクションがあります。ヒープウォーカーのヘッダー領域には、現在のオブジェクトセットに含まれるオブジェクト数などの情報が表示されます。

初期状態では「クラス」ビューが表示されており、これはライブメモリセクションの「全オブジェクト」ビューと似ています。クラスを選択してUse→Selected Instancesを実行すると、そのクラスのインスタンスのみを含む新しいオブジェクトセットが作成されます。ヒープウォーカーにおいて「Use」は常に新しいオブジェクトセットの作成を意味します。

新しいオブジェクトセットに対して、ヒープウォーカーのクラスビューを表示しても、選択したクラスだけがフィルタリングされるだけなのであまり意味がありません。そのため、JProfilerは「新しいオブジェクトセット」ダイアログで別のビューを提案します。このダイアログをキャンセルすると新しいオブジェクトセットは破棄され、前のビューに戻ります。デフォルトでは「アウトゴーイング参照」ビューが提案されますが、他のビューも選択可能です。これは初期表示ビューのためのものであり、後からヒープウォーカーのビューセレクターで自由に切り替えられます。

ヘッダー領域には、現在2つの選択ステップがあることが表示され、保持サイズやディープサイズの計算、または現在のオブジェクトセットによって保持されている全オブジェクトの「Use」リンクが含まれます。後者を選択すると、さらに選択ステップが追加され、複数のクラスが含まれる可能性があるためクラスビューが提案されます。

ヒープウォーカーの下部には、これまでの選択ステップがリスト表示されます。ハイパーリンクをクリックすると任意の選択ステップに戻ることができます。最初のデータセットにはツールバーのGo To Startボタンでもアクセスできます。ツールバーの戻る・進むボタンは、解析中に戻りたい場合に便利です。

クラスビュー

ヒープウォーカー上部のビューセレクターには、現在のオブジェクトセットに対して異なる情報を表示する5つのビューがあります。その最初が「クラス」ビューです。

クラスビューはライブメモリセクションの「全オブジェクト」ビューと似ており、集約レベル選択でクラスをパッケージ単位にグループ化できます。さらに、クラスごとの推定保持サイズも表示可能です。これは、クラスの全インスタンスをヒープから削除した場合に解放されるメモリ量です。Calculate estimated retained sizesハイパーリンクをクリックすると、新たにRetained Size列が追加されます。表示される保持サイズは推定下限値であり、正確な値の計算は非常に時間がかかります。正確な値が必要な場合は、対象のクラスやパッケージを選択し、新しいオブジェクトセットのヘッダーにあるCalculate retained and deep sizesハイパーリンクを使用してください。

1つまたは複数のクラスやパッケージを選択した場合、インスタンス自体、関連するjava.lang.Classオブジェクト、または全保持オブジェクトを選択できます。ダブルクリックは最速の選択モードで、選択したインスタンスを使用します。複数の選択モードが利用可能な場合は、このようにUseドロップダウンメニューがビュー上部に表示されます。

クラスローダー関連の問題を解決する際には、インスタンスをクラスローダーごとにグループ化する必要がよくあります。Inspectionsタブには「Group by class loaders」インスペクションがあり、クラスビューで利用できます。これはその文脈で特に重要です。この解析を実行すると、上部のグルーピングテーブルに全クラスローダーが表示されます。クラスローダーを選択すると、下のビューのデータがそれに応じてフィルタリングされます。他のビューに切り替えてもグルーピングテーブルは維持され、別の選択ステップを実行するとクラスローダーの選択がその選択ステップの一部となります。

割り当て記録ビュー

オブジェクトがどこで割り当てられたかの情報は、メモリリークの疑いを絞り込んだり、メモリ消費を削減しようとする際に重要です。JProfilerヒープスナップショットの場合、「Allocations」ビューで割り当て呼び出しツリーや割り当てホットスポットが表示され、割り当てが記録されたオブジェクトが対象となります。その他のオブジェクトは割り当て呼び出しツリー内の「unrecorded objects」ノードにグループ化されます。HPROF/PHDスナップショットではこのビューは利用できません。

クラスビューと同様に、複数のノードを選択し、上部のUse Selectedボタンで新しい選択ステップを作成できます。「Allocation hot spots」ビューでは、バックトレース内のノードも選択可能です。これにより、選択したバックトレースで終わる呼び出しスタック上で割り当てられた、関連するトップレベルホットスポット内のオブジェクトのみが選択されます。

JProfilerが割り当て記録時に保存できるもう一つの情報は、オブジェクトが割り当てられた時刻です。ヒープウォーカーの「Time」ビューでは、現在のオブジェクトセット内の全記録インスタンスの割り当て時刻のヒストグラムが表示されます。クリック&ドラッグで1つまたは複数の区間を選択し、Use Selectedボタンで新しいオブジェクトセットを作成できます。

時間区間をより正確に選択したい場合は、ブックマークの範囲を指定できます。最初と最後に選択したブックマークの間にある全オブジェクトがマークされます。

Timeビューに加え、割り当て時刻は参照ビューの独立した列としても表示されます。ただし、割り当て時刻の記録はデフォルトで有効になっていません。Timeビューから直接有効化するか、セッション設定ダイアログのAdvanced SettingsMemory Profilingで設定を編集できます。

最大オブジェクトビュー

最大オブジェクトビューは、現在のオブジェクトセット内で最も重要なオブジェクトのリストを表示します。ここでの「最大」とは、それらをヒープから削除した場合に最も多くのメモリが解放されるオブジェクトを意味します。このサイズは保持サイズと呼ばれます。対して、ディープサイズは強参照で到達可能な全オブジェクトの合計サイズです。

各オブジェクトは展開可能で、そのオブジェクトによって保持されている他のオブジェクトへのアウトゴーイング参照が表示されます。このようにして、祖先のいずれかが削除された場合にガベージコレクションされる保持オブジェクトのツリーを再帰的に展開できます。この種のツリーは「ドミネーターツリー」と呼ばれます。このツリー内の各オブジェクトに表示される情報は、アウトゴーイング参照ビューと似ていますが、支配的な参照のみが表示されます。

全ての支配下オブジェクトがドミネーターから直接参照されているとは限りません。例えば、次の図のような参照を考えてみてください。

GCルート直接支配します直接支配します間接的に支配しますObject AObject B2Object B1Object C

オブジェクトAはB1とB2を支配していますが、Cへの直接参照は持っていません。B1とB2の両方がCを参照しています。B1もB2もCを支配していませんが、AはCを支配しています。この場合、B1、B2、CはドミネーターツリーでAの直下の子としてリストされ、CはB1やB2の子としては表示されません。B1とB2については、A内で保持されているフィールド名が表示されます。Cについては、参照ノードに「[transitive reference]」と表示されます。

ドミネーターツリーの各参照ノードの左側には、トップレベルオブジェクトの保持サイズのうち、ターゲットオブジェクトにまだ保持されている割合を示すサイズバーが表示されます。ツリーを深く掘り下げるほど数値は減少します。ビュー設定では、パーセンテージの基準をヒープ全体のサイズに変更できます。

ドミネーターツリーには組み込みのカットオフがあり、親オブジェクトの保持サイズの0.5%未満の保持サイズしか持たない全てのオブジェクトは除外されます。これは、重要でない小さな支配下オブジェクトのリストが長くなりすぎてしまうのを防ぐためです。このようなカットオフが発生した場合、表示されていないオブジェクト数、合計保持サイズ、単一オブジェクトの最大保持サイズを通知する特別な「cutoff」子ノードが表示されます。

単一オブジェクトの表示の代わりに、ドミネーターツリーは最大オブジェクトをクラス単位でグループ化することもできます。ビュー上部のグルーピングドロップダウンにチェックボックスがあり、この表示モードを有効化できます。さらに、トップレベルでクラスローダーによるグルーピングも追加可能です。クラスローダーグルーピングは最大オブジェクトの計算後に適用され、最大オブジェクトのクラスがどのクラスローダーでロードされたかを表示します。特定のクラスローダーの最大オブジェクトだけを解析したい場合は、まず「Group by class loader」インスペクションを利用してください。

最大オブジェクトビュー上部の表示モードセレクターでサンバーストダイアグラムに切り替えることができます。このダイアグラムは同心円状のセグメントリングで構成され、ドミネーターツリー全体の内容を最大深さまで1つの画像で表示します。参照は最も内側のリングから始まり、外側の円周に向かって伝播します。この可視化により、情報密度の高いフラットな視点で参照パターンを発見したり、大きなプリミティブ配列やオブジェクト配列をカラーコーディングで一目で把握できます。

現在のオブジェクトセットがヒープ全体の場合、円の全周は使用済みヒープサイズに対応します。最大オブジェクトビューはヒープ全体の0.1%以上を保持するオブジェクトのみを表示するため、最大オブジェクトに保持されていない全オブジェクトに対応する空のセクターが大きくなります。

任意のリングセグメントをクリックすると、円の新しいルートが設定され、ダイアグラムで表示できる最大深さが拡張されます。ダイアグラム中央の空洞部分をクリックすると前のルートに戻ります。新しいルートが設定されている場合、円の全周はルートオブジェクトの保持サイズに対応します。空のセクターはルートオブジェクトの自己サイズや最大保持オブジェクトリストに存在しない追加オブジェクトを表します。現在のオブジェクトセットがヒープ全体でない場合、円の全周は表示中の最大オブジェクトの合計に対応し、空のセクターは表示されません。

インスタンスやその直近の保持オブジェクトに関する詳細情報は、ダイアグラム右側にマウスオーバーすることで表示されます。マウスがリングセグメント外にある場合、右側リストには最も内側のリングの最大オブジェクトが表示されます。そのリストにマウスオーバーすると対応するリングセグメントがハイライトされ、リスト項目をクリックすると新しいルートが設定されます。新しいオブジェクトセットを作成するには、リングセグメントやリスト項目のコンテキストメニューからアクションを選択できます。

参照ビュー

これまでのビューと異なり、参照ビューは少なくとも1回の選択ステップを実行した場合のみ利用できます。初期オブジェクトセットでは、インカミング・アウトゴーイング参照ビューは全個別オブジェクトを表示してしまい、マージ参照ビューもフォーカスされたオブジェクトセットでなければ意味を持ちません。

アウトゴーイング参照ビューは、IDEのデバッガーで表示されるビューに似ています。オブジェクトを開くと、プリミティブデータや他オブジェクトへの参照が表示されます。任意の参照型を新しいオブジェクトセットとして選択でき、複数のオブジェクトを同時に選択することも可能です。クラスビューと同様に、保持オブジェクトや関連するjava.lang.Classオブジェクトも選択できます。選択したオブジェクトが標準コレクションの場合、全要素を一括選択することもできます。クラスローダーオブジェクトの場合は、ロード済みインスタンス全体を選択するオプションもあります。

null参照のフィールドは、メモリ解析の際に不要な情報となるため、デフォルトでは表示されません。デバッグ目的で全フィールドを見たい場合は、ビュー設定でこの動作を変更できます。

表示インスタンスの単純な選択に加え、アウトゴーイング参照ビューには強力なフィルタリング機能があります。ライブセッションでは、アウトゴーイング・インカミング両参照ビューで高度な操作や表示機能が利用でき、同じ章で説明されています。

インカミング参照ビューはメモリリーク解決の主なツールです。なぜオブジェクトがガベージコレクションされないのかを調べるには、Show Paths To GC Rootボタンでガベージコレクタルートへの参照チェーンを検索できます。メモリリークの章でこの重要なトピックについて詳しく説明しています。

マージ参照

多数の異なるオブジェクトの参照を調べるのは手間がかかるため、JProfilerは現在のオブジェクトセット内全オブジェクトのアウトゴーイング・インカミング参照をマージして表示できます。デフォルトでは、参照はクラス単位で集約されます。クラスのインスタンスが同じクラスの他インスタンスから参照されている場合、  特別なノードが挿入され、元のインスタンスとクラス再帰参照からのインスタンスが表示されます。この仕組みにより、リンクリストのような一般的なデータ構造の内部参照チェーンが自動的に折りたたまれます。

マージ参照をフィールド単位でグループ化することもできます。その場合、各ノードは特定のクラスのフィールドや配列の内容などの参照型になります。標準コレクションでは、集約を妨げる内部参照チェーンが圧縮され、「java.lang.HashMapのmap value」などの参照型が表示されます。クラス集約と異なり、この仕組みはJRE標準ライブラリの明示的にサポートされたコレクションにのみ適用されます。

「Merged outgoing references」ビューでは、インスタンス数は参照先オブジェクトを指します。「Merged incoming references」ビューでは、各行に2つのインスタンス数が表示されます。1つ目は現在のオブジェクトセット内でこのパスで参照されているインスタンス数です。ノード左側のバーアイコンがこの割合を可視化します。矢印アイコンの後ろの2つ目のインスタンス数は、親ノードを参照しているオブジェクト数です。選択ステップ実行時、選択した方法で参照されている現在のオブジェクトセット内のオブジェクトを選択するか、選択した参照を持つオブジェクト(参照保持者)に興味があるかを選択できます。

「Merged dominating references」ビューでは、現在のオブジェクトセット内の一部または全てのオブジェクトをガベージコレクション可能にするために削除すべき参照を特定できます。支配参照ツリーは、最大オブジェクトビューのドミネーターツリーのマージ逆バージョン(クラス単位で集約)と解釈できます。参照矢印は2つのクラス間の直接参照を必ずしも表しませんが、その間に非支配参照を持つ他のクラスが存在する場合もあります。複数のガベージコレクタルートがある場合、現在のオブジェクトセット内の一部または全てのオブジェクトに支配参照が存在しないこともあります。

デフォルトでは「Merged dominating references」ビューはインカミング支配参照を表示し、ツリーを展開することでGCルートに保持されているオブジェクトに到達できます。時には、参照ツリーが多くの異なるパスで同じルートオブジェクトに至る場合もあります。ビュー上部のドロップダウンで「GC roots to objects」表示モードを選択すると、ルートがトップレベル、現在のオブジェクトセットがリーフノードとなる逆の視点が得られます。その場合、参照はトップレベルからリーフノードに向かって進みます。どちらの視点が適切かは、削除したい参照が現在のオブジェクトセットに近いか、GCルートに近いかによります。

インスペクション

「Inspections」ビュー自体はデータを表示しません。他のビューでは利用できないルールに基づいて新しいオブジェクトセットを作成するヒープ解析を複数提供します。例えば、スレッドローカルによって保持されている全オブジェクトを見たい場合などは、参照ビューでは不可能ですが、インスペクションで実現できます。インスペクションは複数のカテゴリにグループ化され、説明文で解説されています。

インスペクションは計算されたオブジェクトセットをグループに分割できます。グループはヒープウォーカー上部のテーブルに表示されます。例えば、「Duplicate strings」インスペクションでは重複した文字列値ごとにグループ化されます。参照ビューにいる場合、選択した文字列値を持つjava.lang.Stringインスタンスが下に表示されます。初期状態ではグループテーブルの最初の行が選択されています。選択を変更すると現在のオブジェクトセットも変わります。グループテーブルのInstance CountSize列で、行を選択した際のオブジェクトセットの規模が分かります。

グループ選択はヒープウォーカーの独立した選択ステップではなく、インスペクションによる選択ステップの一部となります。グループ選択は下部の選択ステップペインに表示され、選択を変更すると即座に更新されます。

グループを作成する各インスペクションは、インスペクションの文脈で最も重要なグループを決定します。これは他の列の自然なソート順と一致しない場合もあるため、グループテーブルのPriority列に数値が表示され、インスペクション用のソート順を強制します。

インスペクションは大規模ヒープでは計算コストが高くなる場合があるため、結果はキャッシュされます。これにより、履歴を遡って以前に計算したインスペクションの結果を待ち時間なく確認できます。

ヒープウォーカーグラフ

インスタンスとその参照関係を最も現実的に表現するのはグラフです。グラフは視覚的な密度が低く、特定の解析には不向きな場合もありますが、オブジェクト間の関係を可視化する最良の方法です。例えば、循環参照はツリーでは解釈が難しいですが、グラフでは一目で分かります。また、ツリー構造ではインカミングかアウトゴーイングのどちらかしか見られませんが、グラフなら両方を同時に確認できます。

ヒープウォーカーグラフは、現在のオブジェクトセットから自動的にオブジェクトを表示したり、オブジェクトセット変更時に自動でクリアされたりしません。アウトゴーイング参照ビュー、インカミング参照ビュー、最大オブジェクトビューから、選択したインスタンスをShow In Graphアクションで手動でグラフに追加します。

グラフ内のパッケージ名はデフォルトで短縮表示されます。CPU呼び出しグラフと同様、ビュー設定で完全表示に切り替え可能です。参照は矢印で描画されます。参照上にマウスを移動すると、該当参照の詳細を表示するツールチップウィンドウが表示されます。参照ビューから手動追加したインスタンスは青色背景で表示され、追加が新しいほど背景色が濃くなります。ガベージコレクタルートは赤色背景、クラスは黄色背景です。

デフォルトでは、参照グラフは現在のインスタンスの直接インカミング・アウトゴーイング参照のみを表示します。任意のオブジェクトをダブルクリックするとグラフが拡張され、そのオブジェクトの直接インカミングまたはアウトゴーイング参照が展開されます(進行方向による)。インスタンスの左右にある展開コントロールで、インカミング・アウトゴーイング参照を個別に開くこともできます。戻りたい場合は、アンドゥ機能でグラフの以前の状態を復元でき、ノードが多すぎて混乱するのを防げます。グラフのトリミングには、全ての未接続ノードや全オブジェクトを削除するアクションも用意されています。

インカミング参照ビューと同様に、グラフにはShow Path To GC Rootボタンがあり、ガベージコレクタルートへの参照チェーンを展開できます。さらに、2つのインスタンスが選択されている場合はFind Path Between Two Selected Nodesアクションが有効になり、有向・無向パスや弱参照を含めた検索も可能です。該当パスが見つかれば赤色で表示されます。

初期オブジェクトセット

ヒープスナップショット取得時、初期オブジェクトセットを制御するオプションを指定できます。割り当て記録が有効な場合、Select recorded objectsチェックボックスで、初期表示オブジェクトを記録済みオブジェクトのみに制限できます。通常、ライブメモリビューと数値が異なりますが、これはヒープウォーカーが参照されていないオブジェクトを削除するためです。未記録オブジェクトもヒープスナップショットには含まれていますが、初期オブジェクトセットには表示されません。選択ステップを進めることで未記録オブジェクトにも到達できます。

さらに、ヒープウォーカーはガベージコレクションを実行し、弱参照オブジェクト(ソフト参照を除く)を削除します。これは、メモリリーク解析時に弱参照オブジェクトが不要なノイズとなるため、通常は望ましい動作です。ただし、弱参照オブジェクトに興味がある場合は、ヒープウォーカーにそれらを保持させることもできます。JVMの4つの弱参照タイプは「soft」「weak」「phantom」「finalizer」であり、どのタイプでオブジェクトを保持するか選択可能です。

弱参照オブジェクトが存在する場合、ヒープウォーカーの「Weak reference」インスペクションで現在のオブジェクトセットから選択または削除できます。

ヒープのマーキング

特定のユースケースで割り当てられたオブジェクトを調べたい場合がよくあります。割り当て記録をユースケースの前後で開始・停止する方法もありますが、より低オーバーヘッドで、他用途のために割り当て記録機能を温存できる優れた方法があります。それがMark Heapアクションです。これはヒープウォーカーの概要やProfilingメニュー、またはトリガーアクションとして利用でき、全ヒープオブジェクトを「old」としてマークします。次回ヒープスナップショット取得時、「new」オブジェクトが明確になります。

以前のヒープスナップショットやMark Heap実行があった場合、ヒープウォーカーのタイトル領域に新規インスタンス数とUse newUse oldという2つのリンクが表示され、指定時点以降に割り当てられたインスタンス、またはそれ以前に割り当てられた生存インスタンスを選択できます。この情報は各オブジェクトセットで利用できるため、まずドリルダウンしてから後でnew/oldインスタンスを選択することも可能です。