jvisualvmでヒープをダンプすると、Runnableへの "Java Frame" GCルート参照を削除するにはどうしたらいいですか?

私は 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がこのリファレンスをリリースするように指示して、ヒープ・ダンプのガベージ・コレクションを正しく行うことができる方法はありますか?

何が起きてる?

3
「インスタンス」パネルでは、収集する必要のあるインスタンスを選択し、右クリックし、コンテキストメニューから「スレッドで表示」を選択できますか?もしそうなら、それは何を言いますか?
追加された 著者 erickson,

2 答え

この文脈において、「フレーム」はスタックフレームを指す。この Runnable は、実行中のスレッドのターゲットではなく(またはそれに加えて)、実行中のスレッドのスタック上のフレーム内のローカル変数に格納されているようです。フレームに関連付けられたメソッドが返ってくると、コレクションに適格になります。


後のコメントに基づいて、私の推測では、あなたのカスタムスレッドプールには、 Runnable が割り当てられているローカル変数があります。おそらくループの外側でスコープが大きすぎ、ループの繰り返しごとにクリアされません( null )。

私はワーカースレッドで以下のようなコードで記述されているものと一致する状況を再現することができます:

Runnable target = null;
while (true) {
  target = queue.take();
  target.run();
}

ループ内にあるように target の宣言をクリーンアップして問題を修正します。

私はコアJavaから Executor 実装に切り替えるか、カスタムスレッドプールの関連コードを修正したい場合はそれを掲示することをお勧めします。

4
追加された
@ErickRobertson JSEの Executor を使用していますか?どの実装クラスですか?
追加された 著者 erickson,
これがJavaのコアクラスのローカル変数に格納されていれば、jvisualvmヒープダンプには表示されませんか?システム内の他のすべてのオブジェクトには、ヒープ内のすべての参照の完全なリストがあります。だから、私はこのオブジェクトへの参照は、ヒープの外側のどこかからのものだと仮定することができます。
追加された 著者 Erick Robertson,
ビンゴ!それはまったく同じパターンではありませんでしたが、これはまさに何が起こっていたかです。この参照は、スレッドが別の Runnable を待っているときにまだ保持されていたローカル変数のスレッドスタックにありました。素晴らしい答えをありがとう!
追加された 著者 Erick Robertson,
ちなみに、私は Executor サービスも見ていきます。私が最初にそれを見たとき、私はそれに完全に圧倒され、ほとんどの機能は必要ありませんでした。今私はそれを見て、それはかなり簡単なようです。スワップとテストを行うコードはほんの数行にすぎないので、私のバックグラウンドプロジェクトのリストに載っています。双方向のベンチマークを行い、より優れたソリューションを決定するのは簡単です。あなたの助けと学びをもう一度感謝します。
追加された 著者 Erick Robertson,

作成したオブジェクトで何をしましたか?あなたはスレッドを作成し、それをポイントしましたか?この場合、run()内のコードの実行を許可してスレッドが停止していることを確認する必要があります。

1
追加された
オブジェクトは、実行する必要のある作業があるときはいつでも、スレッドプールに渡されます。スレッドがダンプされた状態では、オブジェクトは完全に参照解除されており、スレッドプールはアイドル状態です。このオブジェクトのコードには残りの参照はありません。それを実行した最後のスレッドは、最後に実行した実行可能ファイルへの内部参照を保持していましたか?
追加された 著者 Erick Robertson,
それは確かに可能です。もちろん、そうすべきではありません。実験として、実際のRunnableに委譲したラッパーRunnableを作成し、実際のRunnableにラッパーrunnable内の参照を手動で削除するとどうなりますか?あなたの実行ファイルはGCedになり、ラッパーは残っていますか?
追加された 著者 Sarel Botha,
また、エグゼキュータを停止するとどうなりますか?別の実行可能ファイルを実行するとどうなりますか?これは解放されますか?
追加された 著者 Sarel Botha,