15

私たちのプログラムの 1つで、OutOfMemoryあるユーザーのマシンでエラーが発生することがありますが、もちろん私がテストしているときはそうではありません。JProfiler を使用して実行し (これまで使用したことがないため、10 日間の評価ライセンスで)、コード プレフィックスでフィルタリングすると、合計サイズとインスタンス数の両方で最大のチャンクは、特定の単純なクラスの 8000 以上のインスタンスです。 .

JProfiler の [ガベージ コレクション] ボタンをクリックすると、他のクラスのほとんどのインスタンスが消えましたが、これらの特定のインスタンスは消えませんでした。同じインスタンスで再度テストを実行すると、クラスの 4000 以上のインスタンスが作成されましたが、[ガベージ コレクション] をクリックすると、それらは元の 8000 以上のインスタンスを残して消えてしまいました。

これらのインスタンスは、さまざまな段階でさまざまなコレクションにスタックします。それらがガベージコレクションされていないという事実は、何かがコレクションの1つへの参照を保持しているため、オブジェクトへの参照を保持していることを意味するに違いないと思います。

何が参照を保持しているのかを理解する方法はありますか? コードで何を探すべきかの提案と、ある場合は JProfiler でこれを見つける方法を探しています。

4

11 に答える 11

20

ヒープをダンプして検査します。

これを行う方法は複数あると思いますが、ここでは簡単な方法を示します。この説明は MS Windows を対象としていますが、他のオペレーティング システムでも同様の手順を実行できます。

  1. JDK をまだインストールしていない場合は、インストールします。きちんとしたツールがたくさん付属しています。
  2. アプリケーションを起動します。
  3. タスク マネージャーを開き、java.exe (または使用している実行可能ファイル) のプロセス ID (PID) を見つけます。PID がデフォルトで表示されない場合は、[表示] > [列の選択...] を使用して追加します。
  4. jmapを使用してヒープをダンプします。
  5. 生成したファイルでjhatサーバーを起動し、ブラウザーを開いてhttp://localhost:7000 (デフォルトのポートは 7000) にアクセスします。これで、関心のあるタイプと、インスタンスの数、それらへの参照があるものなどの情報を参照できます。

次に例を示します。

C:\dump>jmap -dump:format=b,file=heap.bin 3552

C:\dump>jhat heap.bin
Reading from heap.bin...
Dump file created Tue Sep 30 19:46:23 BST 2008
Snapshot read, resolving...
Resolving 35484 objects...
Chasing references, expect 7 dots.......
Eliminating duplicate references.......
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.

これを解釈するには、Java が使用する配列型の命名法をいくつか理解しておくと役に立ちます。たとえば、クラス [Ljava.lang.Object; 実際にはObject[]型のオブジェクトを意味します。

于 2008-09-30T19:28:17.737 に答える
10

Eclipse メモリ アナライザーを試してください。各オブジェクトが GC ルート (JVM によって保持されているため、ガベージ コレクションされないオブジェクト) にどのように接続されているかを示します。

Eclipse MAT の仕組みの詳細については、 http://dev.eclipse.org/blogs/memoryanalyzer/2008/05/27/automated-heap-dump-analysis-finding-memory-leaks-with-one-click/を参照してください。

于 2008-09-30T19:26:21.127 に答える
5

私はあなたのクラスのコレクション(特に静的なもの)を見ていきます(HashMapsは始めるのに良い場所です)。このコードを例にとってみましょう。

Map<String, Object> map = new HashMap<String, Object>(); // 1 Object
String name = "test";             // 2 Objects
Object o = new Object();          // 3 Objects
map.put(name, o);                 // 3 Objects, 2 of which have 2 references to them

o = null;                         // The objects are still being
name = null;                      // referenced by the HashMap and won't be GC'd

System.gc();                      // Nothing is deleted.

Object test = map.get("test");    // Returns o
test = null;

map.remove("test");               // Now we're down to just the HashMap in memory
                                  // o, name and test can all be GC'd

HashMapまたはその他のコレクションにそのオブジェクトへの参照がある限り、ガベージコレクションは行われません。

于 2008-09-30T18:58:33.547 に答える
3

そこに特効薬はありません。プロファイラーを使用して、これらの不要なオブジェクトを保持するコレクションを特定し、それらが削除されるべきコード内の場所を見つける必要があります。JesperE が言ったように、静的コレクションは最初に確認する場所です。

于 2008-09-30T18:44:26.187 に答える
2

明らかな候補の 1 つは、ファイナライザーを持つオブジェクトです。finalize メソッドが呼び出されている間、そのままにしておくことができます。それらを収集してからファイナライズし (通常は 1 つのファイナライザー スレッドのみで)、再度収集する必要があります。

また、実際にはオブジェクト リクエストを作成するのに十分なメモリがあるにもかかわらず、gc が十分なメモリを収集できなかったため、OOME が発生する可能性があることにも注意してください。そうしないと、パフォーマンスが地に落ちてしまいます。

于 2008-09-30T18:44:23.440 に答える
2

静的コンテナーに注意してください。クラスがロードされている限り、静的コンテナ内のオブジェクトはそのまま残ります。

編集: WeakReference に関する誤ったコメントを削除しました。

于 2008-09-30T18:35:32.900 に答える
1

これに関する記事を読んだばかりですが、どこにあるのか思い出せず申し訳ありません。「EffectiveJava」という本に載っていたのではないかと思います。参照が見つかったら、回答を更新します。

概説した2つの重要な教訓は次のとおりです。

1)finalメソッドは、オブジェクトをカリングするときにgcに何をするかを指示しますが、そうするように要求することも、要求する方法もありません。

2)管理されていないメモリ環境での「メモリリーク」に相当する現代の例は、忘れられた参照です。オブジェクトへのすべての参照を終了時にnullに設定しないと、オブジェクトがカリングされることはありません。これは、独自の種類のコレクション、またはコレクションを管理する独自のラッパーを実装する場合に最も重要です。プール、スタック、またはキューがあり、コレクションからオブジェクトを「削除」するときにバケットをnullに設定しない場合、そのオブジェクトが含まれていたバケットは、そのバケットがに設定されるまでそのオブジェクトを存続させます。別のオブジェクトを参照してください。

免責事項:私は他の回答がこれに言及していることを知っていますが、私はより詳細を提供しようとしています。

于 2008-09-30T19:20:18.533 に答える
1

コレクションについてはすでに触れました。もう1つの見つけにくい場所は、複数のClassLoaderを使用している場合です。これは、すべての参照がなくなるまで、古いクラスローダーをガベージコレクションできない可能性があるためです。

静力学もチェックしてください-これらは厄介です。ロギングフレームワークは、カスタムアペンダーでの参照を維持する可能性のあるものを開いたままにすることができます。

問題を解決しましたか?

于 2009-02-09T07:24:17.417 に答える
1

Java 1.5 でのパフォーマンスの最適化のために、Yourkit Java プロファイラー ( http://www.yourkit.com ) を使用しました。メモリリークに対処する方法に関するセクションがあります。便利だと思います。

http://www.yourkit.com/docs/75/help/performance_problems/memory_leaks/index.jsp

15 日間の評価版を取得できます: http://www.yourkit.com/download/yjp-7.5.7.exe

BR、
~A

于 2008-09-30T19:30:57.307 に答える
0

ガベージ コレクトされた言語で OOM エラーが発生する場合は、通常、コレクタによって考慮されていないメモリがあることを意味します。オブジェクトが Java 以外のリソースを保持している可能性がありますか? その場合、Java オブジェクトがすぐに収集されない場合でもリソースが確実に解放されるように、ある種の「close」メソッドが必要です。

于 2008-09-30T18:37:01.760 に答える