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