148

Java でのメモリ リークをどのように見つけますか (JHat などを使用)。基本的な外観を得るために、JHat にヒープ ダンプをロードしようとしました。ただし、ルート参照 ( ref ) またはそれが呼び出されるものを見つける方法がわかりません。基本的に、数百メガバイトのハッシュ テーブル エントリ ([java.util.HashMap$Entry など) があることがわかりますが、マップはいたるところで使用されています...大きなマップを検索する方法はありますか? 、またはおそらく大きなオブジェクトツリーの一般的なルートを見つけますか?

[編集] わかりました、これまでの回答を読みましたが、私は安っぽい野郎だとだけ言っておきましょう (つまり、JProfiler にお金を払うよりも、JHat の使い方を学ぶことに関心があるということです)。また、JHat は JDK の一部であるため、いつでも使用できます。もちろん、JHat にはブルート フォース以外の方法はありませんが、そうであるとは信じられません。

また、実際に変更 (すべてのマップ サイズのログを追加) して、リークに気付くのに十分な時間実行できるとは思いません。

4

12 に答える 12

132

私はJavaでメモリリークを見つけるために次のアプローチを使用します。私はjProfilerを使用して大成功を収めましたが、グラフ作成機能(差分はグラフ形式で分析する方が簡単です)を備えた特殊なツールであればどれでも機能すると思います。

  1. アプリケーションを起動し、すべての初期化が完了してアプリケーションがアイドル状態になったら、「安定した」状態になるまで待ちます。
  2. メモリリークが発生している疑いのある操作を数回実行して、キャッシュ、DB関連の初期化を実行できるようにします。
  3. GCを実行し、メモリスナップショットを取得します。
  4. 操作を再実行してください。操作の複雑さと処理されるデータのサイズによっては、操作を数回から数回実行する必要がある場合があります。
  5. GCを実行し、メモリスナップショットを取得します。
  6. 2つのスナップショットの差分を実行して分析します。

基本的に、分析は、たとえばオブジェクトタイプによる最大の正の差分から開始し、それらの余分なオブジェクトがメモリに留まる原因を見つける必要があります。

複数のスレッドでリクエストを処理するWebアプリケーションの場合、分析はより複雑になりますが、それでも一般的なアプローチが適用されます。

私は特にアプリケーションのメモリフットプリントを削減することを目的としたかなりの数のプロジェクトを行いましたが、アプリケーション固有の微調整とトリックを使用したこの一般的なアプローチは常にうまく機能しました。

于 2008-09-02T18:49:35.590 に答える
50

質問者様、どのクリックにも 5 分もかからずに応答できるツールを入手することで、潜在的なメモリ リークの発見がはるかに容易になると言わざるを得ません。

人々はいくつかのツールを提案しているので (私は JDK と JProbe のトライアルで入手したのでビジュアル wm だけを試しました)、私は Eclipse プラットフォーム上に構築されたフリー/オープン ソース ツール、Memory Analyzer (SAP メモリとして参照されることもあります) を提案する必要があります。アナライザー) http://www.eclipse.org/mat/で入手できます。

このツールの本当に優れている点は、最初に開いたときにヒープ ダンプにインデックスを付けたことです。これにより、各オブジェクトを 5 分待たずに、保持されたヒープのようなデータを表示できました (ほとんどすべての操作は、私が試した他のツールよりもはるかに高速でした)。 .

ダンプを開くと、最初の画面に最大のオブジェクト (保持されたヒープをカウント) の円グラフが表示され、大きすぎるオブジェクトにすばやく移動して快適に作業できます。また、リークの可能性が高いと思われるものを見つける機能もあり、便利だと思いますが、ナビゲーションは私にとって十分だったので、実際には入りませんでした.

于 2008-09-11T08:29:56.513 に答える
14

ツールは大きな助けになります。

ただし、ツールを使用できない場合があります。ヒープダンプが非常に大きいためにツールがクラッシュしたり、シェルアクセスしかできない本番環境でマシンのトラブルシューティングを試みたりします。

その場合、hprofダンプファイルを回避する方法を知るのに役立ちます。

SITESBEGINを探します。これにより、どのオブジェクトが最も多くのメモリを使用しているかがわかります。ただし、オブジェクトはタイプだけでまとめられているわけではありません。各エントリには「トレース」IDも含まれています。次に、その「TRACE nnnn」を検索して、オブジェクトが割り当てられたスタックの上位数フレームを確認できます。多くの場合、オブジェクトがどこに割り当てられているかを確認すると、バグを見つけて完了します。また、-Xrunhprofのオプションを使用して、スタックに記録されるフレーム数を制御できることに注意してください。

割り当てサイトをチェックアウトしても問題がない場合は、予期しない参照チェーンを見つけるために、これらのライブオブジェクトの一部からルートオブジェクトへの後向き連鎖を開始する必要があります。これはツールが本当に役立つところですが、同じことを手作業で行うことができます(まあ、grepを使用して)。ルートオブジェクトは1つだけではありません(つまり、ガベージコレクションの対象ではないオブジェクト)。スレッド、クラス、およびスタックフレームはルートオブジェクトとして機能し、それらが強く参照するものはすべて収集できません。

連鎖を行うには、HEAP DUMPセクションで、トレースIDが不正なエントリを探します。これにより、OBJまたはARRエントリが表示され、16進数で一意のオブジェクト識別子が表示されます。そのIDのすべての出現箇所を検索して、オブジェクトへの強い参照を持っている人を見つけます。リークがどこにあるかがわかるまで、分岐するときにこれらの各パスを逆方向にたどります。ツールがとても便利な理由がわかりますか?

静的メンバーは、メモリリークの繰り返しの違反者です。実際、ツールがなくても、静的なMapメンバーのコードを調べるのに数分を費やす価値があります。地図を大きくすることはできますか?エントリをクリーンアップするものはありますか?

于 2008-09-02T18:30:42.530 に答える
10

ほとんどの場合、エンタープライズ アプリケーションでは、指定された Java ヒープは最大 12 ~ 16 GB の理想的なサイズよりも大きくなります。これらの大きな Java アプリケーションで NetBeans プロファイラーを直接動作させるのは難しいと感じました。

しかし、通常、これは必要ありません。jdk に付属の jmap ユーティリティを使用して「ライブ」ヒープ ダンプを取得できます。つまり、jmap は GC の実行後にヒープをダンプします。アプリケーションで何らかの操作を行い、操作が完了するまで待ってから、別の「ライブ」ヒープ ダンプを取得します。Eclipse MAT などのツールを使用して、ヒープダンプをロードし、ヒストグラムを並べ替え、増加したオブジェクトや最も高いオブジェクトを確認します。これにより手がかりが得られます。

su  proceeuser
/bin/jmap -dump:live,format=b,file=/tmp/2930javaheap.hrpof 2930(pid of process)

このアプローチには 1 つの問題しかありません。巨大なヒープ ダンプは、ライブ オプションを使用しても、開発ラップに転送するには大きすぎる可能性があり、開くには十分なメモリ/RAM を備えたマシンが必要になる場合があります。

そこで、クラス ヒストグラムの出番です。jmap ツールを使用して、ライブ クラス ヒストグラムをダンプできます。これにより、メモリ使用量のクラス ヒストグラムのみが得られます。基本的に、参照をチェーンするための情報はありません。たとえば、char配列を先頭に配置する場合があります。そして、その下のどこかにある String クラス。自分で接続を描画する必要があります。

jdk/jdk1.6.0_38/bin/jmap -histo:live 60030 > /tmp/60030istolive1330.txt

2 つのヒープ ダンプを取得する代わりに、上記のように 2 つのクラス ヒストグラムを取得します。次に、クラス ヒストグラムを比較して、増加しているクラスを確認します。Java クラスをアプリケーション クラスに関連付けることができるかどうかを確認します。これはかなり良いヒントになります。2 つの jmap ヒストグラム ダンプを比較するのに役立つ pythons スクリプトを次に示します。ヒストグラムパーサー.py

最後に、JConolse や VisualVm などのツールは、時間の経過に伴うメモリの増加を確認し、メモリ リークがあるかどうかを確認するために不可欠です。最後に、問題がメモリ リークではなく、高いメモリ使用量である場合もあります。これには、GC ロギングを有効にします。G1GC などのより高度で新しい圧縮 GC を使用します。また、jstat などの jdk ツールを使用して、GC の動作をライブで確認できます。

jstat -gccause pid <optional time interval>

-jhat、jmap、フル GC、Humongous 割り当て、G1GC についての Google へのその他の参照

于 2015-06-24T06:15:44.530 に答える
5

JProbe、YourKit、AD4J、JRockit Mission Controlなど、リークを見つけるのに役立つツールがあります。最後は私が個人的に一番よく知っているものです。優れたツールを使用すると、リークしているものと、リークしているオブジェクトが割り当てられている場所を簡単に特定できるレベルまでドリルダウンできます。

HashTables、Hashmapsなどを使用することは、Javaでメモリを正確にリークできる数少ない方法の1つです。手作業でリークを見つけなければならない場合は、ハッシュマップのサイズを定期的に印刷し、そこからアイテムを追加して削除するのを忘れたものを見つけます。

于 2008-09-02T17:48:30.337 に答える
4

まあ、マップを変更するときにマップのサイズのログを追加し、適切なサイズを超えて拡大しているマップのログを検索するという、ローテクな解決策が常にあります。

于 2008-09-02T17:39:50.530 に答える
1

NetBeansにはプロファイラーが組み込まれています。

于 2008-09-02T18:30:34.760 に答える
0

割り当てを追跡するメモリプロファイラーを実際に使用する必要があります。JProfilerを見てください。それらの「ヒープウォーカー」機能は素晴らしく、すべての主要なJavaIDEと統合されています。それは無料ではありませんが、それほど高価でもありません(単一ライセンスで499ドル)-それほど洗練されていないツールでリークを見つけるのに非常に迅速に苦労して500ドル相当の時間を費やします。

于 2008-09-02T17:46:46.223 に答える
0

JProfiler を使用したメモリ リークの検出に関するこのスクリーン キャストを確認してください。@Dima Malenko Answer の視覚的な説明です。

注: JProfiler はフリーウェアではありませんが、試用版は現状に対応できます。

于 2019-11-21T04:38:10.227 に答える