JProfiler ヘルプDownload

メモリリークの発見


通常のメモリ使用量とメモリリークを区別するのは、しばしば簡単ではありません。しかし、過剰なメモリ使用量とメモリリークは同じ症状を持っているため、同じ方法で分析することができます。分析は2つのステップで進行します: 疑わしいオブジェクトを特定し、それらのオブジェクトがなぜヒープに残っているのかを見つけ出すことです。

新しいオブジェクトの発見

メモリリークのあるアプリケーションが実行されていると、時間とともにますます多くのメモリを消費します。 メモリ使用量の増加を検出するには、VMテレメトリーと 差分機能を「すべてのオブジェクト」と「記録されたオブジェクト」 ビューで使用するのが最適です。これらのビューを使用すると、問題があるかどうか、またその深刻さを判断できます。時には、コールヒストグラムテーブルの差分列が問題の手がかりを与えてくれることもあります。

メモリリークのより深い分析には、ヒープウォーカーの機能が必要です。特定のユースケースに関するメモリリークを詳細に調査するには、「ヒープをマーク」機能が最適です。これにより、特定の過去の時点からヒープに残っている新しいオブジェクトを特定できます。これらのオブジェクトについては、それらがまだ正当にヒープに存在するのか、誤った参照がそれらを生かし続けているのかを確認する必要があります。

興味のあるオブジェクトのセットを分離するもう一つの方法は、割り当て記録を通じて行うことです。 ヒープスナップショットを取る際に、すべての記録されたオブジェクトを表示するオプションがあります。しかし、割り当て記録を特定のユースケースに限定したくないかもしれません。また、割り当て記録は高いオーバーヘッドを持つため、ヒープをマークアクションは比較的に影響が小さくなります。最後に、ヒープウォーカーは、ヒープをマークした場合、新しいものを使用および古いものを使用ハイパーリンクを使用して、任意の選択ステップで古いオブジェクトと新しいオブジェクトを選択できます。

最大のオブジェクトの分析

メモリリークが利用可能なヒープを埋めると、プロファイルされたアプリケーションの他のタイプのメモリ使用量を圧倒します。その場合、新しいオブジェクトを調べる必要はなく、単に最も重要なオブジェクトを分析するだけです。

メモリリークは非常に遅い速度で発生し、長い間支配的にならないことがあります。そのようなメモリリークをプロファイリングして可視化するまで待つのは実用的ではないかもしれません。OutOfMemoryErrorがスローされたときにHPROFスナップショットを自動的に保存するJVMの組み込み機能を使用すると、通常のメモリ消費よりもメモリリークが重要なスナップショットを取得できます。実際、常に追加することをお勧めします。

-XX:+HeapDumpOnOutOfMemoryError

VMパラメータや本番システムに追加することで、開発環境で再現が難しいメモリリークを分析する方法を持つことができます。

メモリリークが支配的である場合、ヒープウォーカーの「最大のオブジェクト」ビューのトップオブジェクトには、誤って保持されたメモリが含まれます。最大のオブジェクト自体は正当なオブジェクトであるかもしれませんが、それらのドミネーターツリーを開くと、リークされたオブジェクトにたどり着きます。単純な状況では、ヒープの大部分を含む単一のオブジェクトがあります。たとえば、マップがオブジェクトをキャッシュするために使用され、そのキャッシュが決してクリアされない場合、マップは最大のオブジェクトのドミネーターツリーに表示されます。

ガベージコレクタルートからの強い参照チェーンの発見

オブジェクトが問題になるのは、それが強く参照されている場合のみです。「強く参照されている」とは、ガベージコレクタルートからオブジェクトへの参照チェーンが少なくとも1つあることを意味します。「ガベージコレクタルート」(略してGCルート)は、JVMが知っている特別な参照です。

GCルートからの参照チェーンを見つけるには、「インカミング参照」ビューまたはヒープウォーカーグラフでGCルートへのパスを表示アクションを使用できます。このような参照チェーンは実際には非常に長いことがあるため、「インカミング参照」ビューで一般的により簡単に解釈できます。参照は下から上位レベルのオブジェクトに向かって指します。検索の結果である参照チェーンのみが展開され、同じレベルの他の参照はノードが閉じて再度開かれるか、コンテキストメニューですべてのインカミング参照を表示アクションが呼び出されるまで表示されません。

参照ノードで使用されるGCルートのタイプやその他の用語の説明を得るには、ツリーレジェンドを使用してください。

ツリー内のノードを選択すると、非モーダルのツリーレジェンドが選択されたノードで使用されているすべてのアイコンと用語を強調表示します。ダイアログ内の行をクリックすると、下部に説明が表示されます。

重要なタイプのガベージコレクタルートは、スタックからの参照、JNIを通じてネイティブコードによって作成された参照、および現在使用中のライブスレッドやオブジェクトモニターなどのリソースです。さらに、JVMは重要なシステムを維持するためにいくつかの「スティッキー」参照を追加します。

クラスとクラスローダーには特別な循環参照スキームがあります。クラスは、そのクラスローダーによってロードされたクラスがライブインスタンスを持たない場合に、クラスローダーと一緒にガベージコレクトされます。

  • そのクラスローダーによってロードされたクラスがライブインスタンスを持たない
  • クラスローダー自体がそのクラスによってのみ参照されている
  • java.lang.Classオブジェクトがクラスローダーのコンテキストでのみ参照されている

ほとんどの状況では、クラスは興味のあるGCルートへのパスの最後のステップです。 クラス自体はGCルートではありません。しかし、カスタムクラスローダーが使用されていないすべての状況では、それらをそのように扱うのが適切です。これは、ガベージコレクタルートを検索する際のJProfilerのデフォルトモードですが、ルートオプションダイアログへのパスで変更できます。

GCルートへの最短パスを解釈するのに問題がある場合は、追加のパスを検索できます。一般的に、GCルートへのすべてのパスを検索することは推奨されません。なぜなら、それは大量のパスを生成する可能性があるからです。

ライブメモリビューとは対照的に、ヒープウォーカーは未参照のオブジェクトを表示しません。しかし、ヒープウォーカーは強く参照されているオブジェクトだけを表示するわけではありません。デフォルトでは、ヒープウォーカーはソフト参照によってのみ参照されているオブジェクトも保持しますが、弱い参照、ファントム参照、またはファイナライザ参照によってのみ参照されているオブジェクトは排除します。ソフト参照はヒープが枯渇しない限りガベージコレクトされないため、それらを含めることで大きなヒープ使用量を説明できるようにしています。ヒープスナップショットを取る際に表示されるオプションダイアログで、この動作を調整できます。

ヒープウォーカーに弱く参照されているオブジェクトを含めることは、デバッグ目的で興味深いかもしれません。後で弱く参照されているオブジェクトを削除したい場合は、「弱い参照によって保持されているオブジェクトを削除」検査を使用できます。

GCルートへのパスを検索する際、ヒープウォーカーオプションダイアログでオブジェクトを保持するために選択された参照タイプが考慮されます。このようにして、GCルート検索へのパスは常にオブジェクトがヒープウォーカーに保持された理由を説明できます。GCルート検索へのパスのオプションダイアログでは、許容される参照タイプをすべての弱い参照に拡大できます。

オブジェクトセット全体の排除

これまでのところ、単一のオブジェクトについてのみ検討してきました。多くの場合、メモリリークの一部である同じタイプの多くのオブジェクトを持つことがあります。現在のオブジェクトセット内の他のオブジェクトにも有効である場合が多いです。興味のあるオブジェクトが異なる方法で参照されている場合のより一般的なケースでは、「マージされた支配参照」ビューが、現在のオブジェクトセットをヒープに保持している参照を見つけるのに役立ちます。

支配参照ツリーの各ノードは、その参照を排除した場合に現在のオブジェクトセット内のどれだけのオブジェクトがガベージコレクションの対象になるかを示します。複数のガベージコレクタルートによって参照されているオブジェクトは、支配的なインカミング参照を持たない場合があるため、ビューはオブジェクトの一部にしか役立たないか、場合によっては空であることもあります。その場合、マージされたインカミング参照ビューを使用し、ガベージコレクタルートを一つずつ排除する必要があります。