web-dev-qa-db-ja.com

スレッドローカル削除?

ThreadLocalを使用する場合、完了時またはsetを実行するときは、常にremove()を呼び出す必要があります。とにかく古い値が置き換えられるため、removeは冗長ですか?

20
Jim

setは常に古い値を置き換えます。

これは

  • Calendar.set()およびDate.set()
  • BitSet.set()
  • List.set()
  • セッター

削除しないとGCされないということですか?

スレッドが終了するまで削除されません。 remove()を呼び出さないと、消えることはありません。

これがメモリリークであるかどうかは、プログラムによって異なります。大きなスレッドローカルオブジェクトを使用して多数のスレッドを作成する必要がありますが、それが問題になるために何らかの理由で必要ありませんでした。例えば1 KBのオブジェクトを持つ1000スレッドは、最大1 MBを浪費する可能性がありますが、この種のことを行う場合、これは設計上の問題を示唆しています。


メモリリークが発生する可能性があるのは、唯一の場所です。

for (int i = 0; ; i++) {
    // don't subclass Thread.
    new Thread() {
        // this is somewhat pointless as you are defining a ThreadLocal per thread.
        final ThreadLocal<Object> tlObject = new ThreadLocal<Object>() {
        };

        public void run() {
            tlObject.set(new byte[8 * 1024 * 1024]);
        }
    }.start();
    Thread.sleep(1);
    if (i % 1000 == 0) {
        System.gc();
        System.out.println(i);
    }
}

-verbosegc印刷します。

[Full GC 213548K->49484K(3832192K), 0.0334194 secs]
39000
[GC 2786060K->82412K(3836864K), 0.0132035 secs]
[GC 2815569K->107052K(3836544K), 0.0212252 secs]
[GC 2836162K->131628K(3837824K), 0.0199268 secs]
[GC 2867613K->156204K(3837568K), 0.0209828 secs]
[GC 2886894K->180780K(3838272K), 0.0191244 secs]
[GC 2911942K->205356K(3838080K), 0.0187482 secs]
[GC 421535K->229932K(3838208K), 0.0192605 secs]
[Full GC 229932K->49484K(3838208K), 0.0344509 secs]
40000

注:完全なGCの後のサイズは同じです49484K

上記の場合、ThreadLocalを参照するThreadを参照するThreadLocalがあります。ただし、スレッドが停止しているため、メモリリークは発生しません。これは、スレッドが関連オブジェクトになるためです。つまり、A-> BおよびB-> Aの場合です。

上記の例を数分間ループで実行すると、GCレベルがかなり移動しましたが、最小サイズはまだ小さかったです。

15
Peter Lawrey

ThreadLocalにはMapcurrentThreadvalueがあるため、それを使用していたスレッドの値を削除しないと、メモリーリーク。

ThreadLocalクラスはThreadによって定義されたクラスから値を置くため、常にremoveを呼び出す必要がありますThreadLocal.Values localValues; Thisまた、スレッドおよび関連オブジェクトの参照を保持します。

ThreadLocalのソースコードから

値はnullに設定され、基になるエントリは引き続き存在します。

15
Amit Deshpande

set:このスレッドローカル変数の現在のスレッドのコピーを指定された値に設定します。

そのメモリ位置にあったものはすべて、setを通過したものによって上書きされることを意味します

4
Tony The Lion

いいえ「常にset()の代わりにremove() "を呼び出す必要はありません

そうすることでメモリリークが発生することを恐れる場合は、次のようになります javadoc

各スレッドは、スレッドが動作していてThreadLocalインスタンスにアクセスできる限り、スレッドローカル変数のコピーへの暗黙的な参照を保持します。スレッドがなくなると、スレッドローカルインスタンスのすべてのコピーがガベージコレクションの対象になります(これらのコピーへの他の参照が存在しない場合)。

したがって、remove()を呼び出さなくても、スレッドローカルインスタンスが適切にガベージコレクションされるのを妨げることはなく、本質的にメモリリークを引き起こすこともありません。

この「暗黙の参照」メカニズムに WeakReferences を使用するThreadLocal実装を確認することもできます。

しかしスレッドプールとの一貫性に注意してください

スレッドプールでのみset()メソッドを使用する場合は、同じスレッドを使用して別の「ワークユニット」でオーバーライドするのではなく、remove() ThreadLocalインスタンスを使用することをお勧めします。 。何らかの理由でsetメソッドが呼び出されず、ThreadLocalが、それが属していないコンテキスト/処理にアタッチされたままになる状況を回避したい場合があるためです。

2
Rémi Bantos

スレッドの次の実行でremoveしようとしている変数が常にsetである場合、それを削除する心配はありません。 setはその値を上書きします。

ただし、一部の状況でのみその変数を設定している場合(たとえば、特定の種類のリクエストのみを処理する場合)、たとえば、スレッドが元に戻されたときに変数が留まらないように、変数を削除すると便利な場合があります。プール。

2
Xavi López

簡単にします。何らかの理由でThreadLocalを拡張する場合は、remove()を使用します。 Vanilla ThreadLocalでは、set(null)を使用します。基本的に、拡張ThreadLocal.remove()を使用しないと、メモリリークが発生する可能性があります(ClassLoaderのものが最も可能性が高い)

理由の詳細が必要な場合は、コメントを投稿してください。

1
bestsss

スレッドが完了すると、threadLocalMapはスレッドで行われます。削除する必要はありません。ただし、スレッドをリサイクルに使用する場合は、threadLocalMapの値を削除する必要があります。

0
lupeng ge