JProfiler帮助文档

メモリプロファイリング

ヒープ上のオブジェクトに関する情報を取得する方法は2つあります。1つは、プロファイリングエージェントが各オブジェクトの分配とガベージコレクションを追跡する方法です。JProfilerでは、これを「分配記録」と呼びます。これにより、オブジェクトがどこで分配されたかを知ることができ、一時オブジェクトに関する統計を作成することもできます。もう1つは、JVMのプロファイリングインターフェースが「ヒープスナップショット」を取得し、すべてのライブオブジェクトとその参照を調査する方法です。この情報は、オブジェクトがなぜガベージコレクションされないのかを理解するために必要です。

分配記録とヒープスナップショットの両方は高価な操作です。分配記録は、java.lang.Objectコンストラクタを計測し、ガベージコレクタがプロファイリングインターフェースに継続的に報告しなければならないため、実行時の特性に大きな影響を与えます。これが、デフォルトでは分配が記録されず、記録の開始と停止を明示的に行う必要がある理由です。ヒープスナップショットの取得は一度限りの操作です。しかし、JVMを数秒間停止させる可能性があり、取得したデータの分析にはヒープのサイズに応じて比較的長い時間がかかることがあります。

JProfilerはメモリ分析を2つのビューセクションに分けています。「ライブメモリ」セクションは定期的に更新できるデータを表示し、「ヒープウォーカー」セクションは静的なヒープスナップショットを表示します。分配記録は「ライブメモリ」セクションで制御されますが、記録されたデータはヒープウォーカーによっても表示されます。

メモリプロファイリングで解決できる最も一般的な3つの問題は、メモリリークの発見、メモリ消費の削減、一時オブジェクトの作成の削減です。最初の2つの問題については、主にヒープウォーカーを使用し、JVM内の最大のオブジェクトを保持しているのが誰で、どこで作成されたかを確認します。最後の問題については、記録された分配を表示するライブビューにのみ依存することができます。なぜなら、それはすでにガベージコレクションされたオブジェクトを含むからです。

インスタンス数の追跡

ヒープ上のオブジェクトの概要を把握するために、「すべてのオブジェクト」ビューはすべてのクラスとそのインスタンス数のヒストグラムを表示します。このビューに表示されるデータは分配記録で収集されるのではなく、インスタンス数のみを計算するミニヒープスナップショットを実行することによって収集されます。ヒープが大きいほど、この操作に時間がかかるため、このビューは現在の値で自動的に更新されません。

メモリリークを探す際には、インスタンス数を時間の経過とともに比較したいことがよくあります。すべてのクラスに対してそれを行うには、ビューの差分機能を使用できます。すべてのオブジェクトの2つのダンプが同時に選択されると、差分列が挿入され、インスタンス数のヒストグラムはマーク時の基準値を緑色で表示します。すべてのオブジェクトの新しいダンプを取得すると、最も古い選択されたダンプが選択されたままになり、すべてのオブジェクトの新しいダンプとの違いが表示されます。

ダンプセレクタは、ダンプが取得された時間を示します。ダブルクリックでダンプにラベルを追加し、識別を容易にすることができます。すべてのオブジェクトのダンプは、トリガーアクションController APIでもトリガーでき、ラベルを指定することもできます。

一方、「記録されたオブジェクト」ビューは、分配記録を開始した後に分配されたオブジェクトのインスタンス数のみを表示します。分配記録を停止すると、新しい分配は追加されませんが、ガベージコレクションは引き続き追跡されます。このようにして、特定の使用ケースでヒープに残るオブジェクトを確認できます。オブジェクトが長時間ガベージコレクションされないことに注意してください。Run GCツールバーボタンを使用して、このプロセスを加速できます。動的に更新されるほとんどのビューと同様に、表示されるデータの更新を停止するためのFreezeツールバーボタンが利用可能です。

Mark Currentツールバーボタンを使用すると、「記録されたオブジェクト」ビューでも選択された基準に対して差分列を表示できます。選択されたクラスに対しては、コンテキストメニューのAdd Selection to Class Trackerアクションを使用して時間解決されたグラフを表示することもできます。

分配スポット

分配記録がアクティブな場合、JProfilerはオブジェクトが分配されるたびにコールスタックを記録します。スタックウォーキングAPIからの正確なコールスタックは使用しません。なぜなら、それは非常に高価だからです。代わりに、CPUプロファイリングに設定されているのと同じメカニズムが使用されます。つまり、コールスタックはコールツリーフィルタに従ってフィルタリングされ、実際の分配スポットはコールスタックに存在しないメソッドにある可能性があります。なぜなら、それは無視されたかコンパクトフィルタされたクラスからのものだからです。しかし、これらの変更は直感的に理解しやすいです。コンパクトフィルタされたメソッドは、コンパクトフィルタされたクラスへのさらなる呼び出しで行われるすべての分配に対して責任を負います。

サンプリングを使用すると、分配スポットは概算となり、混乱を招くことがあります。時間測定とは異なり、特定のクラスがどこで分配されるか、どこでされないかについて明確なアイデアを持っていることがよくあります。サンプリングは統計的な絵を描くため、java.util.HashMap.getが自分のクラスを分配するような、見かけ上不可能な分配スポットを見ることがあるかもしれません。正確な数値とコールスタックが重要な分析のためには、計測とともに分配記録を使用することをお勧めします。

CPUプロファイリングと同様に、分配コールスタックはコールツリーとして提示され、呼び出し回数と時間ではなく、分配回数と分配メモリが表示されます。CPUコールツリーとは異なり、分配コールツリーは自動的に表示および更新されません。なぜなら、ツリーの計算がより高価だからです。JProfilerは、すべてのオブジェクトだけでなく、選択されたクラスやパッケージに対しても分配ツリーを表示できます。他のオプションとともに、これはJProfilerに現在のデータから分配ツリーを計算するように依頼した後に表示されるオプションダイアログで設定されます。

CPUコールツリーの有用な特性は、各ノードが子ノードに費やされた時間を含むため、上から下に累積時間を追跡できることです。デフォルトでは、分配ツリーも同じように動作し、各ノードが子ノードによって行われた分配を含みます。分配がコールツリーの深い葉ノードでのみ行われる場合でも、数値は上に伝播します。このようにして、分配コールツリーの枝を開くときにどのパスを調査する価値があるかを常に確認できます。「自己分配」は、実際にノードによって行われ、その子孫によって行われないものです。CPUコールツリーと同様に、パーセンテージバーは異なる色でそれらを表示します。

分配コールツリーでは、特に選択されたクラスの分配を表示する場合、分配がまったく行われないノードが多く存在することがあります。これらのノードは、実際の分配が行われたノードに至るまでのコールスタックを示すためにのみ存在します。これらのノードはJProfilerで「ブリッジ」ノードと呼ばれ、上記のスクリーンショットのように灰色のアイコンで表示されます。場合によっては、分配の累積が邪魔になり、実際の分配スポットのみを表示したいことがあります。分配ツリーのビュー設定ダイアログには、その目的のために非累積数値を表示するオプションがあります。有効にすると、ブリッジノードは常にゼロの分配を表示し、パーセンテージバーを持ちません。

分配ホットスポットビューは、分配コールツリーとともにポピュレートされ、選択されたクラスを作成する責任があるメソッドに直接焦点を当てることができます。記録されたオブジェクトビューと同様に、分配ホットスポットビューは現在の状態をマークし、時間の経過に伴う違いを観察することをサポートしています。差分列がビューに追加され、Mark Current Valuesアクションが呼び出された時点からホットスポットがどのように変化したかを示します。デフォルトでは分配ビューは定期的に更新されないため、Calculateツールバーボタンをクリックして新しいデータセットを取得し、基準値と比較します。自動更新はオプションダイアログで利用可能ですが、大きなヒープサイズには推奨されません。

分配記録率

すべての分配を記録することは、かなりのオーバーヘッドを追加します。多くの場合、分配の総数は重要ではなく、相対的な数値が問題を解決するのに十分です。これが、JProfilerがデフォルトで10回に1回の分配のみを記録する理由です。これにより、すべての分配を記録する場合と比較してオーバーヘッドが約1/10に減少します。すべての分配を記録したい場合、または目的に対してさらに少ない分配が十分である場合は、記録されたオブジェクトビューや分配コールツリーおよびホットスポットビューのパラメータダイアログで記録率を変更できます。

この設定は、セッション設定ダイアログの「Advanced Settings->Memory profiling」ステップでも見つけることができ、オフラインプロファイリングセッション用に調整できます。

分配記録率は、「記録されたオブジェクト」と「記録されたスループット」のVMテレメトリに影響を与え、その値は設定された分数で測定されます。スナップショットを比較する際には、最初のスナップショットの分配率が報告され、必要に応じて他のスナップショットがそれに応じてスケーリングされます。

分配されたクラスの分析

分配ツリーと分配ホットスポットビューを計算する際には、事前に見たい分配のクラスまたはパッケージを指定する必要があります。特定のクラスにすでに焦点を当てている場合にはうまく機能しますが、事前の概念なしに分配ホットスポットを見つけようとする場合には不便です。1つの方法は、「記録されたオブジェクト」ビューを見て、選択されたクラスまたはパッケージの分配ツリーまたは分配ホットスポットビューに切り替えるためのコンテキストメニューのアクションを使用することです。

もう1つの方法は、すべてのクラスの分配ツリーまたは分配ホットスポットから始めて、Show classesアクションを使用して選択された分配スポットまたは分配ホットスポットのクラスを表示することです。

分配されたクラスのヒストグラムは、コールツリー分析として表示されます。このアクションは他のコールツリー分析からも機能します。

クラス分析ビューは静的であり、分配ツリーとホットスポットビューが再計算されても更新されません。Reload Analysisアクションは、最初に分配ツリーを更新し、その後新しいデータから現在の分析ビューを再計算します。

ガベージコレクションされたオブジェクトの分析

分配記録はライブオブジェクトを表示するだけでなく、ガベージコレクションされたオブジェクトに関する情報も保持します。これは、一時的な分配を調査する際に役立ちます。多くの一時オブジェクトを分配することは、かなりのオーバーヘッドを生じさせる可能性があるため、分配率を下げることでパフォーマンスが大幅に向上することがよくあります。

記録されたオブジェクトビューでガベージコレクションされたオブジェクトを表示するには、活性セレクタをGarbage collected objectsまたはLive and garbage collected objectsに変更します。分配コールツリーと分配ホットスポットビューのオプションダイアログには、同等のドロップダウンがあります。

ただし、JProfilerはデフォルトではガベージコレクションされたオブジェクトの分配ツリー情報を収集しません。なぜなら、ライブオブジェクトのみのデータははるかに少ないオーバーヘッドで維持できるからです。「Allocation Call Tree」または「Allocation Hotspots」ビューでガベージコレクションされたオブジェクトを含むモードに活性セレクタを切り替えると、JProfilerは記録タイプを変更することを提案します。これはプロファイリング設定の変更であるため、変更をすぐに適用することを選択した場合、以前に記録されたすべてのデータがクリアされます。事前にこの設定を変更したい場合は、セッション設定ダイアログの「Advanced Settings」->「Memory Profiling」で行うことができます。

次のステップ: ヒープウォーカー

より高度なタイプの質問は、オブジェクト間の参照を含むことになります。たとえば、記録されたオブジェクト、分配ツリー、および分配ホットスポットビューに表示されるサイズはシャローサイズです。これらはクラスのメモリレイアウトのみを含み、参照されたクラスは含まれません。クラスのオブジェクトが実際にどれだけ重いかを確認するには、保持サイズ、つまりそれらのオブジェクトがヒープから削除された場合に解放されるメモリ量を知りたいことがよくあります。

この種の情報は、すべてのオブジェクトをヒープ上で列挙し、高価な計算を行う必要があるため、ライブメモリビューでは利用できません。その作業はヒープウォーカーによって処理されます。ライブメモリビューの関心のあるポイントからヒープウォーカーにジャンプするには、Show in Heap Walkerツールバーボタンを使用できます。それにより、ヒープウォーカーの同等のビューに移動します。

ヒープスナップショットが利用できない場合は、新しいヒープスナップショットが作成されます。それ以外の場合は、JProfilerは既存のヒープスナップショットを使用するかどうかを尋ねます。

いずれにせよ、ライブメモリビューとヒープウォーカーの数値が非常に異なることを理解することが重要です。ヒープウォーカーはライブメモリビューとは異なる時点でスナップショットを表示するだけでなく、すべての参照されていないオブジェクトを排除します。ガベージコレクタの状態によっては、参照されていないオブジェクトがヒープのかなりの部分を占めることがあります。