私は jvisualvm
を使ってアプリケーションのメモリリークをチェックしています。ヒープ・ダンプを実行すると、ガベージ・コレクションされているはずのオブジェクトが開いていることがあります。
When I do a "Show Nearest GC Root" command on them, it shows me that the root is a class which I defined which implements the interface Runnable. The reference is listed as (java frame)
, which I know has something to do with threading. When I expand the tree for this node, it opens up and shows
. So it seems pretty clear that this is not a reference I'm keeping open, but something internal to Java.
jvisualvmにリストされているGC Rootオブジェクトは、 AnalyticNode extends Node
型であり、 Node implements Runnable
です。このルートオブジェクトは、「フレーム」という単語が使用されているにもかかわらず、AWT、Swing、または他の重量のあるユーザーインターフェイスコンポーネントとは何の関係もありません。この場合、「フレーム」という語はスレッディングを意味します。
それで、Javaは最後のRunnableへの参照を、これを保持しているどこかで保持していますか? Javaがこのリファレンスをリリースするように指示して、ヒープ・ダンプのガベージ・コレクションを正しく行うことができる方法はありますか?
何が起きてる?
この文脈において、「フレーム」はスタックフレームを指す。この Runnable
は、実行中のスレッドのターゲットではなく(またはそれに加えて)、実行中のスレッドのスタック上のフレーム内のローカル変数に格納されているようです。フレームに関連付けられたメソッドが返ってくると、コレクションに適格になります。
後のコメントに基づいて、私の推測では、あなたのカスタムスレッドプールには、 Runnable
が割り当てられているローカル変数があります。おそらくループの外側でスコープが大きすぎ、ループの繰り返しごとにクリアされません( null
)。
私はワーカースレッドで以下のようなコードで記述されているものと一致する状況を再現することができます:
Runnable target = null;
while (true) {
target = queue.take();
target.run();
}
ループ内にあるように target
の宣言をクリーンアップして問題を修正します。
私はコアJavaから Executor
実装に切り替えるか、カスタムスレッドプールの関連コードを修正したい場合はそれを掲示することをお勧めします。