9

私はCallableオブジェクトをに送信していますがThreadPoolExecutor、それらはメモリに残っているようです。

Eclipse用のMATツールでヒープダンプを見ると、CallableオブジェクトがFutureTask$Sync呼び出し可能な変数によって参照されていることがわかります。これは、の同期変数FutureTask$Syncによって参照されます。これは、のthis$0変数によって参照されます。FutureTaskFutureTaskFutureTask$Sync

私はこれについて読んだことがあり(ここここ、そしてSOで) 、呼び出し可能オブジェクトが's submit()FutureTaskにラップされThreadPoolExecutor、呼び出し可能オブジェクトへの参照を永久に保持しているようです。

私が混乱しているのは、FutureTaskガベージコレクションを確実に取得して、呼び出し可能オブジェクトをメモリに保持し続けないようにし、呼び出し可能オブジェクトがメモリに保持している可能性のあるものを保持する方法です。

私の特定の状況について詳しく説明するためにThreadPoolExecutor、必要に応じて送信されたすべてのタスクをキャンセルできるように実装しようとしています。私はSOや他の場所で見つけたいくつかの異なる方法を試しました。たとえば、エグゼキュータを完全にシャットダウンし(などで)、先物のリストを保持し、それらすべてに対してキャンセルを呼び出してから、shutdown()先物のリストをクリアします。理想的には、シャットダウンする必要はなく、必要に応じてクリアする必要があります。shutdownNow()submit()cancel()

これらの方法のすべてが違いを生むわけではないようです。呼び出し可能オブジェクトをプールに送信すると、それが固執してしまう可能性が高くなります。

私は何が間違っているのですか?

ありがとう。

編集:

要求に応じて、これがThreadPoolExecutorのコンストラクターです。

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
    super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}

さらにテストした後、ThreadPoolExecutorに送信されたタスクを終了させて​​も、リークがないことがわかります。とにかくそれらをキャンセルしようとすると:

shutdownNow()

または、将来への参照を保存し、後でキャンセルを呼び出す:

Future referenceToCancelLater = submit(task);
...
referenceToCancelLater.cancel(false);

または、次のような方法でキューから削除します。

getQueue.drainTo(someList)

また

getQueue.clear()

または先物への保存された参照をループして呼び出します:

getQueue.remove(task)

これらのケースのいずれかにより、FutureTaskは上記のように固執します。

したがって、これらすべての本当の問題は、FutureTaskがガベージコレクションされ、永久にリークされないように、ThreadPoolExecutorからアイテムを適切にキャンセルまたは削除する方法です。

4

4 に答える 4

4

この投稿によると、エグゼキュータでパージを呼び出すことができます。

于 2012-12-16T01:03:10.873 に答える
1

回避策として、次のようなことを行うことができます。

class ClearingCallable<T> implements Callable<T> {
    Callable<T> delegate;
    ClearingCallable(Callable<T> delegate) {
        this.delegate = delegate;
    }

    T call() {
        try {
            return delegate.call();
        } finally {
            delegate = null;
        }
    }
}
于 2011-02-07T00:07:55.747 に答える
1

何も動かなかったので、次の解決策を思いつきました。大まかな概要は次のとおりです。ThreadPoolExecutorに配列を作成し、キューにあるランナブルを追跡しました。次に、キューをキャンセルする必要があるときに、ループして、各実行可能ファイルでcancelメソッドを呼び出しました。私の場合、これらのランナブルはすべて私が作成したカスタムクラスであり、cancelメソッドは単にcancelフラグを設定しました。キューが次に処理するキューを起動すると、ランナブルの実行時に、キューがキャンセルされたことがわかり、実際の作業がスキップされます。

したがって、すべての実行可能ファイルは、キャンセルされたことがわかると、1つずつすばやくフラッシュされます。

おそらく最高の解決策ではありませんが、それは私にとってはうまくいき、メモリをリークしません。

于 2011-03-07T23:46:12.810 に答える
0

参照:https ://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html

Futureは、非同期計算の結果を表します。getメソッドを使用して結果を取得しない場合、メモリリークが発生します。

非同期の結果を消費したくない場合は、RunnableinstallCallableを使用してください。

于 2016-07-05T02:11:46.737 に答える