5

この簡単なサンプル コードは、問題を示しています。を作成しArrayBlockingQueue、 を使用してこのキューでデータを待機するスレッドを作成しますtake()。ループが終了した後、理論的にはキューとスレッドの両方をガベージ コレクションできますが、実際にはすぐにOutOfMemoryError. これがGCされるのを妨げているのは何ですか?どうすれば修正できますか?

/**
 * Produces out of memory exception because the thread cannot be garbage
 * collected.
 */
@Test
public void checkLeak() {
    int count = 0;
    while (true) {

        // just a simple demo, not useful code.
        final ArrayBlockingQueue<Integer> abq = new ArrayBlockingQueue<Integer>(2);
        final Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    abq.take();
                } catch (final InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();

        // perform a GC once in a while
        if (++count % 1000 == 0) {
            System.out.println("gc");
            // this should remove all the previously created queues and threads
            // but it does not
            System.gc();
        }
    }
}

Java 1.6.0 を使用しています。

更新: 数回繰り返した後に GC を実行しますが、これは役に立ちません。

4

7 に答える 7

8

スレッドはトップレベルのオブジェクトです。それらは「特別」であるため、他のオブジェクトと同じ規則には従いません。それらを「生きている」(つまり、GCから安全に)保つために参照に依存しません。スレッドは、終了するまでガベージ コレクションを取得しません。スレッドがブロックされているため、サンプル コードでは発生しません。もちろん、スレッドオブジェクトがガベージコレクションされていないため、それによって参照される他のオブジェクト (この場合はキュー) もガベージコレクションできません。

于 2009-04-24T12:57:02.503 に答える
5

ArrayBlockingQueue<Integer> abqスレッドは、エントリが入るまですべてブロックされるため、スレッドを無期限に作成しています。したがって、最終的にはOutOfMemoryError.

(編集)

作成した各スレッドは、abqキューまで 1 つのエントリとしてブロックされるため、終了することはありません。abqスレッドが実行されている場合、GC は、キューやスレッド自体を含め、スレッドが参照しているオブジェクトを収集しません。

于 2009-04-24T09:08:07.983 に答える
2
abq.put(0);

あなたの一日を救うはずです。

スレッドはすべてキューで待機しますtake()が、それらのキューには何も入れません。

于 2009-04-24T09:20:50.120 に答える
0

スレッドを開始すると、ループが新しいスレッドを作成し続けている間、これらすべての新しいスレッドが非同期で実行されます。

コードがロックされているため、スレッドはシステム内のライフ リファレンスであり、収集できません。ただし、スレッドが何らかの作業を行っていたとしても、スレッドが作成されたときと同じ速さで終了する可能性は低いため (少なくともこのサンプルでは)、GC はすべてのメモリを収集できず、最終的に OutOfMemoryException で失敗します。

できるだけ多くのスレッドを作成することは、効率的でも効率的でもありません。保留中のすべての操作を並行して実行する必要がない場合は、スレッド プールとランナブルのキューを使用して処理することをお勧めします。

于 2009-04-24T09:03:03.407 に答える
0

while ループは無限ループであり、新しいスレッドを継続的に作成します。スレッドが作成されるとすぐにスレッドの実行を開始しますが、スレッドによるタスクの完了にかかる時間は、スレッドの作成にかかる時間よりも長くなります。

また、while ループ内で宣言することにより、abq パラメーターで何をしていますか?

あなたの編集やその他のコメントに基づいています。System.gc() は GC サイクルを保証しません。あなたのスレッドの実行速度が作成速度よりも遅いという私の声明を読んでください。

take() メソッドのコメント「このキューに要素が存在しない場合は待機して、このキューの先頭を取得して削除します」を確認しました。ArrayBlockingQueue を定義しているようですが、それに要素を追加していないため、すべてのスレッドがそのメソッドを待機しているだけです。それが OOM を取得する理由です。

于 2009-04-24T09:09:20.663 に答える