質問

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

編集]わかりました、私はこれまでのところ答えを読みましたが、私は安いろくでなしだと言ってみましょう(私はJProfilerの代金を支払うよりもJHATの使用方法を学ぶことに興味があります)。また、JHat は JDK の一部であるため、いつでも利用できます。もちろん、JHat では総当たり以外に方法がない場合は別ですが、そんなことがあり得るとは信じられません。

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

役に立ちましたか?

解決

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

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

基本的に分析は、オブジェクトタイプごとの最大の正の差分から開始し、それらの余分なオブジェクトがメモリに残る原因を特定する必要があります。

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

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

他のヒント

質問者さん、クリックに応答するのに 5 分もかからないツールを入手すると、潜在的なメモリ リークを見つけるのがはるかに簡単になると言わざるを得ません。

人々がいくつかのツールを提案しているので (JDK と JProbe のトライアル版で入手したので、私は Visual wm のみを試しました)、Eclipse プラットフォーム上に構築された無料/オープンソース ツールである Memory Analyzer (SAP メモリとも呼ばれます) を提案する必要があると思いました。アナライザー)で利用可能 http://www.eclipse.org/mat/ .

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

ダンプを開くと、最初の画面に最大のオブジェクト (保持されているヒープをカウント) を含む円グラフが表示され、快適にするには大きすぎるオブジェクトまですばやくナビゲートできます。また、「漏洩の可能性のある容疑者を見つける」機能もあり、これは便利だと思いますが、ナビゲーションだけで十分だったので、あまり深くは入りませんでした。

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

ただし、ツールを使用できない場合があります。ヒープ ダンプが大きすぎてツールがクラッシュする場合、シェル アクセスしかできない運用環境でマシンのトラブルシューティングを行おうとしている場合などです。

その場合、hprof ダンプ ファイルの回避方法を知っておくと役立ちます。

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

アロケーション サイトをチェックアウトして、何も問題が見つからなかった場合は、予期しない参照チェーンを見つけるために、これらのライブ オブジェクトの一部からルート オブジェクトへの逆方向チェーンを開始する必要があります。ここではツールが非常に役立ちますが、同じことを手動で (まあ、grep を使用して) 実行することもできます。ルート オブジェクト (つまり、ガベージ コレクションの対象外のオブジェクト) は 1 つだけではありません。スレッド、クラス、スタック フレームはルート オブジェクトとして機能し、それらが強く参照するものは収集できません。

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

静的メンバーはメモリ リークの常習犯です。実際、ツールがなくても、静的な Map メンバーのコードを調べるのに数分を費やす価値はあります。マップは大きくなる可能性がありますか?そのエントリをクリーンアップするものはありますか?

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

しかし、通常、これは必要ありません。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 ヒストグラム ダンプを比較するのに役立つ Python スクリプトです。 ヒストグラムパーサー.py

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

jstat -gccause pid <optional time interval>

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

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

HashTables や Hashmaps などを使用することは、Java で実際にメモリをリークできる数少ない方法の 1 つです。手動でリークを見つけなければならない場合は、HashMap のサイズを定期的に出力し、そこから項目を追加して削除し忘れた箇所を見つけます。

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

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

実際には、割り当てを追跡するメモリ プロファイラを使用する必要があります。を見てみましょう Jプロファイラー - 「ヒープ ウォーカー」機能は優れており、主要な Java IDE のすべてと統合されています。無料ではありませんが、それほど高価でもありません (1 ライセンスあたり 499 ドル)。洗練されていないツールを使用してリークを見つけるのに苦労すると、すぐに 500 ドル相当の時間を費やすことになります。

チェックしてみてはいかがでしょうか jコンソール. 。これは JDK の一部でもあり、jhat と組み合わせてメモリ/参照リークを見つけるのに役立つことがわかりました。こちらもご覧ください これ ブログの記事。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top