Java 线程本地删除?

Java 线程本地删除?,java,multithreading,thread-local,Java,Multithreading,Thread Local,使用ThreadLocal时,我是否应该在完成或设置时始终调用remove(),以替换旧值,因此remove是多余的?设置始终替换旧值 这对我们来说是正确的 Calendar.set()和Date.set()的值 BitSet.set() List.set() 二传手 你的意思是不删除它就不会被GCD 直到螺纹死了,它才会被移除。如果不调用remove(),它不会在您身上消失 这是否是内存泄漏取决于您的程序。您将不得不创建大量具有大型线程本地对象的线程,而出于某种原因,这些对象并不需要。e、

使用
ThreadLocal
时,我是否应该在完成或设置
时始终调用
remove()
,以替换旧值,因此
remove
是多余的?

设置
始终替换旧值

这对我们来说是正确的

  • Calendar.set()和Date.set()的值
  • BitSet.set()
  • List.set()
  • 二传手
你的意思是不删除它就不会被GCD

直到螺纹死了,它才会被移除。如果不调用remove(),它不会在您身上消失

这是否是内存泄漏取决于您的程序。您将不得不创建大量具有大型线程本地对象的线程,而出于某种原因,这些对象并不需要。e、 g.1000个线程和一个1KB的对象可能会浪费多达1MB的空间,但是如果你在做这种事情,这就意味着一个设计问题


唯一可能发生内存泄漏的地方是

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);
    }
}
注:完整GC后的大小相同
49484K

在上述情况下,您将有一个ThreadLocal,它引用了ThreadLocal的线程。但是,当线程死了时,它不会导致内存泄漏,因为它变成了一个对象,即当a->B和B->a时


我在循环中运行了上述示例几分钟,GC级别移动了很多,但最小大小仍然很小。

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


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

传递的内容覆盖,因为
ThreadLocal
具有
currentThread
Map
value
,现在如果您不删除正在使用它的线程中的值,那么它将创建内存泄漏

您应该始终调用remove,因为ThreadLocal类从ThreadLocal.values localValues定义的Thread类中放入值这还将导致保留线程和关联对象的引用

ThreadLocal

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


如果您试图
删除的变量在线程的下一次执行中总是
set
,我就不必担心删除它<代码>设置
将覆盖其值

但是,如果您仅在某些情况下(例如,仅处理特定类型的请求时)设置该变量,则删除该变量可能会很方便,以便在将线程放回池中时,该变量不会停留在周围。

我将使其变得简单: 如果出于任何原因扩展ThreadLocal,请使用
remove()
。在本地使用
set(null)
。 基本上不在扩展的ThreadLocal上使用
ThreadLocal.remove()
,可能会导致内存泄漏(最有可能是类加载器泄漏)


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

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

如果您担心这样做会导致内存泄漏,下面是

只要线程处于活动状态且ThreadLocal实例可访问,每个线程都持有对其线程局部变量副本的隐式引用;线程消失后,其线程本地实例的所有副本都将接受垃圾收集(除非存在对这些副本的其他引用)

因此,不调用remove()不会阻止对线程本地实例进行正确的垃圾收集,也不会从本质上导致内存泄漏

您还可以看看ThreadLocal实现,它使用这种“隐式引用”机制

但是注意与线程池的一致性

仅对线程池使用set()方法时,您可能更希望删除()ThreadLocal实例,而不是在另一个使用同一线程的“工作单元”中重写它。
因为您可能希望避免由于某种原因而不调用set方法,并且您的ThreadLocal仍然附加到它不属于的上下文/处理。

如果线程已完成,则将使用该线程完成
threadLocalMap
。您不需要删除它。但是如果线程被循环使用,你需要删除
threadLocalMap

的值,你的意思是没有
remove
它不会被GCed?这不是关于值,而是关于
ThreadLocal
中有参考的
ThreadLocal
,如果你不调用remove,它将永远不会被GCed。@Jim问得好,已经添加了一个答案。Peter,泄漏可能发生在扩展的ThreadLocals上,因为该线程将保留对的引用。想想取消部署和动态类加载器,ThreadLocal实际上并不包含对象。该对象保存在线程本身的ThreadLocalMap中,其中ThreadLocal对象是键。当线程停止时,它的所有线程本地对象都可能变得不可访问(除非它们保存在其他位置)。ThreadLocal不是
final
,但我看到它子类化的唯一一次是定义一个initialValue。如果这是在线程的上下文中定义的,则会出现问题,但对线程进行子分类通常被认为是个坏主意。ThreadLocalMap中的条目扩展了WeakReference,并且该值在ThreadLocalMap中具有硬引用。条目[]--因为该值将引用类加载器,而类加载器将引用ThreadLocal-->#1。不过,您需要动态类加载来充分利用它。我不理解您对
set(null)
remove
的区别。调用
set
之前是否应该调用
set(null)
?为什么会导致泄漏?@bestsss有趣的区别。@Jim,如果不通过
set(null)
remove()
删除对象,线程将继续运行,这将是最重要的
[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