jhat
ツールは、ヒープ・スナップショット内のオブジェクト・トポロジを参照するための便利な手段を提供します。このツールはヒープ解析ツール(HAT)にかわるものです。
jhat
ユーティリティの詳細は、jhat
コマンドのマニュアル・ページを参照してください。
このツールは、バイナリ形式のヒープ・ダンプ(jmap -dump
で生成されたヒープ・ダンプなど)を解析します。
このユーティリティは、意図しないオブジェクト保持のデバッグに役立つ可能性があります。この用語は、もう不要になっているけれども、ルート・セットからの一部のパス経由での参照のためにライブに保たれているオブジェクトを記述するために使用されます。これは、オブジェクトが不要になった後もオブジェクトへの意図しない静的参照が残っている場合、ObserverやListenerで不要になった対象からの登録解除が失敗した場合、オブジェクトを参照しているThreadが終了すべきときに終了しない場合などに発生することがあります。意図しないオブジェクト保持は、Java言語ではメモリー・リークに相当します。
次の項では、jhatユーティリティのトラブルシューティング手法を説明します。
このツールには標準クエリーがいくつか用意されています。たとえば、ルート・セットから指定されたオブジェクトまでのすべての参照パスを表示する「ルート」クエリーは、不要なオブジェクト保持の発見に特に役立ちます。
標準クエリーに加え、Object Query Language (OQL)インタフェースで独自のカスタム・クエリーを開発することもできます。
jhat
コマンドを発行すると、このユーティリティにより、指定されたTCPポート上でHTTPサーバーが起動されます。その後、任意のブラウザを使ってサーバーに接続し、指定したヒープ・ダンプに対してクエリーを実行できます。
例2-17は、jhat
を実行してsnapshot.hprof
という名前のヒープ・ダンプ・ファイルを分析する方法を示しています。
例2-17 jhatによるヒープ・ダンプの分析
$ jhat snapshot.hprof
Started HTTP server on port 7000
Reading from java_pid2278.hprof...
Dump file created Fri May 19 17:18:38 BST 2006
Snapshot read, resolving...
Resolving 6162194 objects...
Chasing references, expect 12324 dots................................
Eliminating duplicate references.....................................
Snapshot resolved.
Server is ready.
この時点でjhat
により、ポート7000上でHTTPサーバーが起動されています。ブラウザをhttp://localhost:7000
にポイントしてjhat
サーバーに接続します。
サーバーに接続されている場合は、標準クエリーの実行(「標準クエリー」を参照)またはOQLクエリーの作成(「カスタム・クエリー」」を参照)ができます。「すべてのクラス」クエリーがデフォルトで表示されます。
jhat
サーバーに接続したら、次の標準クエリーを実行できます。
「すべてのクラス」クエリー
デフォルト・ページの「すべてのクラス」クエリーには、ヒープ内に存在しているすべてのクラス(プラットフォーム・クラスは除く)が表示されます。このリストは完全修飾クラス名でソートされ、パッケージ別に分けられます。クラスの名前をクリックすると、「クラス」クエリーに移動します。
このクエリーの2番目のバリアントには、プラットフォーム・クラスが含まれます。プラットフォーム・クラスには、完全修飾名がjava
、sun.
、javax.swing.
、char[
などの接頭辞で始まるクラスが含まれます。この接頭辞のリストは、/resources/platform_names.txt
という名前のシステム・リソース・ファイルに含まれています。このリストをオーバーライドするには、JARファイル内でリストを置き換えるか、またはjhat
の呼出し時にクラス・パス上で置換が先に発生するように調整してください。
「クラス」クエリー
「クラス」クエリーには、クラスに関する情報が表示されます。これにはスーパー・クラス、サブクラス、インスタンス・データ・メンバー、静的データ・メンバーが含まれます。このページから、任意の参照先クラスに移動したり、「インスタンス」クエリーに移動したりできます。
「オブジェクト」クエリー
「オブジェクト」クエリーでは、ヒープ上にあったオブジェクトに関する情報が提供されます。ここから、オブジェクトのクラスに移動したり、オブジェクト・メンバーの値に移動したりできます。現在のオブジェクトを参照しているオブジェクトに移動することもできます。おそらく最も価値の高いクエリーは、末尾にある「ルート」クエリー(ルート・セットからの参照チェーン)です。
注意: 「オブジェクト」クエリーでは、オブジェクトの割当て時点でのスタック・バックトレースも提供されます。
「インスタンス」クエリー
「インスタンス」クエリーには、特定のクラスのすべてのインスタンスが表示されます。allInstances
バリアントには、特定クラスのサブクラスのインスタンスも含まれます。ここから、元のクラスに戻ったり、いずれかのインスタンスの「オブジェクト」クエリーに移動したりできます。
「ルート」クエリー
「ルート」クエリーには、ルート・セットから特定のオブジェクトまでの参照チェーンが表示されます。これは、特定のオブジェクトに到達可能なルート・セットの各メンバーに1つのチェーンを提供します。このツールではこれらのチェーンの計算時に深さ優先探索が行われるため、最小長の参照チェーンが提供されます。
「ルート」クエリーには、弱参照を除外するもの(「ルート」)、それらを含めるもの(「すべてのルート」)という2つの種類があります。弱参照とは、その参照対象がファイナライズ可能になり、ファイナライズされ、そして再利用されることを阻止しない参照オブジェクトです。弱参照によってのみ参照されるオブジェクトは通常、保持の対象とはみなされていません。ガベージ・コレクタはスペースが必要になるとすぐにそれを収集できるからです。
注: これはおそらく、意図しないオブジェクト保持をデバッグするうえで、jhat でもっとも価値の高いクエリーです。保持されているオブジェクトを見つけたら、このクエリーで保持されている理由がわかります。 |
「到達可能なオブジェクト」クエリー
このクエリーは「オブジェクト」クエリーからアクセス可能であり、特定のオブジェクトから到達可能なすべてのオブジェクトの推移閉包を表示します。このリストはサイズの降順(各サイズ内ではアルファベット順)にソートされます。末尾には、すべての到達可能オブジェクトの合計サイズが表示されます。これは、少なくともオブジェクトのトポロジが単純なシステムでは、メモリー内でのオブジェクトの実行時フットプリント合計を決定する際に役立つ可能性があります。
このクエリーの価値がもっとも高まるのは、-exclude
コマンド行オプションと組み合わせて使用する場合です。これは、分析対象のオブジェクトがObservableである場合などに役立ちます。デフォルトではすべてのObserverが到達可能となり、それらが合計サイズにカウントされます。-exclude
オプションを使えば、データ・メンバーjava.util.Observable.obs
とjava.util.Observable.arr
を除外できます。
「すべてのクラスのインスタンス数」クエリー
このクエリーでは、システム内のプラットフォーム・クラス以外のすべてのクラスのインスタンス数が表示されます。そのソート順は、インスタンス数の降順になります。意図しないオブジェクト保持の問題を突きとめるよい方法は、プログラムを様々な入力で長時間実行した後、ヒープ・ダンプを要求することです。すべてのクラスのインスタンス数を確認すると、インスタンス数が予想より多いクラスがいくつかあることに気づく場合があります。その場合はそれらを(おそらく「ルート」クエリーを使って)分析し、保持されている理由を確認できます。このクエリーのバリアントには、プラットフォーム・クラスが含まれます。
プラットフォーム・クラスの詳細は、「「すべてのクラス」クエリー」を参照してください。
「すべてのルート」クエリー
このクエリーでは、弱参照を含むルート・セットのすべてのメンバーが表示されます。
弱参照の詳細は、「「すべてのルート」クエリー」を参照してください。
「新規インスタンス」クエリー
「新規インスタンス」クエリーは、2つのヒープ・ダンプ付きでjhat
サーバーを呼び出した場合にのみ利用できます。このクエリーは「インスタンス」クエリーに似ていますが、新しいインスタンスだけを表示する点が異なります。インスタンスは、それが2番目のヒープ・ダンプ内にあり、かつ同じIDを持つ同じ型のオブジェクトがベースライン・ヒープ・ダンプ内にない場合に、新規とみなされます。オブジェクトのIDは、オブジェクトを一意に識別する32ビットまたは64ビットの整数です。
「ヒストグラム」クエリー
組み込みヒストグラムおよびファイナライザ・ヒストグラム・クエリーも、役立つ情報を提供します。
組み込みObject Query Language (OQL)インタフェースで独自のカスタム・クエリーを開発できます。最初のページの「OQLクエリーの実行」ボタンをクリックするとOQLクエリー・ページが表示され、そこでカスタム・クエリーを作成して実行できます。例2-18に示すように、OQLのヘルプ機能は、組込み関数について説明します。
select
文の構文は次のとおりです。
jhat
から有用な情報を得るには、アプリケーションと、そこで使用されているライブラリやAPIに関するある程度の知識が要求されることがよくあります。jhat
を使用すると、次の2つの重要な質問に回答できます。
何がオブジェクトをライブに保っているのか。
オブジェクト・インスタンスを表示しているときに、このオブジェクトへの参照を示すタイトルのセクションに一覧表示されているオブジェクトをチェックすれば、このオブジェクトを直接参照しているオブジェクトを確認できます。より重要なことは、「ルート」クエリーを使用してルート・セットから特定のオブジェクトまでの参照チェーンを確認できることです。これらの参照チェーンは、ルート・オブジェクトからこのオブジェクトへのパスを示します。これらのチェーンがあれば、どのようにすればルート・セットからオブジェクトに到達できるかをすばやく確認できます。
前述したように、弱参照は「ルート」クエリーから除外されますが、「すべてのルート」クエリーには含まれます。弱参照とは、その参照対象がファイナライズ可能になり、ファイナライズされ、そして再利用されることを阻止しない参照オブジェクトです。オブジェクトが弱参照によってのみ参照される場合、ガベージ・コレクタはスペースが必要になるとすぐにそれを収集できます。
jhat
ツールは、ルート・セット参照チェーンを、ルートの種類に基づいて次の順序でソートします。
Javaクラスの静的データ・メンバー。
Javaのローカル変数。これらのルートでは、それらの担当のスレッドが表示されます。スレッドはJavaオブジェクトなので、このリンクはクリック可能です。このため、たとえばスレッドの名前に容易に移動できます。
ネイティブ静的値。
ネイティブのローカル変数。ここでも、そのようなルートはそれらのスレッドで識別されます。
このオブジェクトはどこで割り当てられたのか。
オブジェクト・インスタンスが表示されている場合、オブジェクトの割当て元を示すタイトルのセクションに、スタック・トレースの形式で割当てサイトが表示されます。このようにして、オブジェクトがどこで作成されたかを確認できます。
注意: この割当てサイトの情報は、heap=all
オプションを使用してHPROFでヒープ・ダンプが作成された場合にのみ入手できます。このHPROFオプションにはheap=dump
オプションとheap=sites
オプションの両方が含まれます。HPROFとそのオプションの詳細は、「HPROF」を参照してください。
単一のオブジェクト・ダンプを使ってリークを特定できない場合の別のアプローチは、一連のダンプを収集し、各ダンプの合間に作成されたオブジェクトに焦点を絞ることです。jhat
ツールでは-baseline
オプションを使ってこの機能が提供されています。
2つのダンプがHPROFによって同じVMインスタンスから生成された場合、-baseline
オプションを使えばそれらを比較できます。同じオブジェクトがどちらのダンプにも現れる場合、それは報告される新規オブジェクトのリストから除外されます。1つのダンプがベースラインとして指定され、そのベースラインの取得以降に2番目のダンプで作成されたオブジェクトに分析を集中できます。
例2-19では、ベースラインの指定方法を示しています。
前述の例では、2つのダンプはsnapshot.hprof
ファイル内にあり、それらは#1
と#2
をファイル名の末尾に付加することによって区別されます。
2つのヒープ・ダンプ付きでjhat
が起動された場合、「すべてのクラスのインスタンス数」クエリーに、その型の新規オブジェクト数である追加列が含まれます。インスタンスは、それが2番目のヒープ・ダンプ内にあり、かつ同じIDを持つ同じ型のオブジェクトがベースライン内にない場合に、新規とみなされます。新規カウントをクリックすると、jhat
はその型の新しいオブジェクトを一覧表示します。このときインスタンスごとに、それがどこで割り当てられたか、これらの新規オブジェクトが参照しているオブジェクト、および新規オブジェクトを参照している他のオブジェクトを表示できます。
一般に、-baseline
オプションは、識別する必要のあるオブジェクトが連続するダンプの合間に作成された場合に非常に役立つ可能性があります。