Threadlocal删除?

时间:2012-09-14 12:44:32

标签: java multithreading thread-local

使用ThreadLocal时,我应该在完成后或remove()时始终致电set,无论如何都会更换旧值,因此remove是多余的?

7 个答案:

答案 0 :(得分:15)

因为ThreadLocalMap currentThreadvalue,所以如果您不删除正在使用它的线程中的值,那么它将创建一个内存泄漏。

  

您应该始终调用删除,因为 ThreadLocal 类会放置 ThreadLocal.Values localValues定义的线程类中的值; 这也将导致保留Thread和相关对象的引用。

来自ThreadLocal

的源代码
  

该值将设置为null,基础条目仍然存在。

答案 1 :(得分:14)

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,它引用引用ThreadLocal的Thread。然而,由于线程已死,因此它不会导致内存泄漏,因为它成为一个关注对象,即当A - > B和B - &gt;甲

我在一个循环中运行上面的例子几分钟,GC水平移动很多,但最小尺寸仍然很小。

答案 2 :(得分:3)

set:将此线程局部变量的当前线程副本设置为指定值。

意味着该内存位置中的内容现在将被您通过set

所覆盖的内容覆盖

答案 3 :(得分:1)

如果您尝试remove的变量在线程的下一次执行中始终为set,我不会担心将其删除。 set会覆盖其值。

但是如果你只是在一些环境中设置那个变量(例如,当只处理特定类型的请求时),删除它可能很方便,因此当它(例如,线程是回到游泳池。

答案 4 :(得分:1)

我会说得很简单: 如果因任何原因扩展ThreadLocal,请使用remove()。在vanilla ThreadLocal上使用set(null)。 基本上不在扩展 ThreadLocal上使用ThreadLocal.remove()会导致内存泄漏(最有可能是ClassLoader)

如果您需要更多详细信息,请发表评论。

答案 5 :(得分:1)

,您不必“总是调用 remove()”,而不必使用 set()

如果您担心内存泄漏,javadoc就是这样

  

只要线程是活动的并且ThreadLocal实例是可访问的,则每个线程都对其线程局部变量的副本持有隐式引用。线程消失后,其所有线程本地实例的副本都将进行垃圾回收(除非存在对这些副本的其他引用)。

因此,不调用 remove()不会阻止对线程本地实例进行正确的垃圾收集,并且自然不会导致内存泄漏。

您还可以查看ThreadLocal实现,该实现使用WeakReferences来实现“隐式引用”机制

但是当心与线程池的一致性

仅将 set()方法与线程池一起使用,您可能更喜欢 remove()一个ThreadLocal实例,而不是使用以下方法在另一个“工作单元”中覆盖它:相同的线程。 因为您可能想要避免由于某种原因而未调用set方法并且您的ThreadLocal仍附加于它不属于的上下文/处理的情况。

答案 6 :(得分:0)

如果线程完成,threadLocalMap将与线程一起完成。您不需要删除它。但是,如果线程用于回收,则需要删除threadLocalMap的值。