`
yangyi
  • 浏览: 112656 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

关于ThreadLocal的内存泄露

阅读更多
ThreadLocal是一种confinement,confinement和local及immutable都是线程安全的(如果JVM可信的话)。因为对每个线程和value之间存在hash表,而线程数量未知,从表象来看ThreadLocal会存在内存泄露,读了代码,发现实际上也可能会内存泄露。

事实上每个Thread实例都具备一个ThreadLocal的map,以ThreadLocal Instance为key,以绑定的Object为Value。而这个map不是普通的map,它是在ThreadLocal中定义的,它和普通map的最大区别就是它的Entry是针对ThreadLocal弱引用的,即当外部ThreadLocal引用为空时,map就可以把ThreadLocal交给GC回收,从而得到一个null的key。

这个threadlocal内部的map在Thread实例内部维护了ThreadLocal Instance和bind value之间的关系,这个map有threshold,当超过threshold时,map会首先检查内部的ThreadLocal(前文说过,map是弱引用可以释放)是否为null,如果存在null,那么释放引用给gc,这样保留了位置给新的线程。如果不存在slate threadlocal,那么double threshold。除此之外,还有两个机会释放掉已经废弃的threadlocal占用的内存,一是当hash算法得到的table index刚好是一个null key的threadlocal时,直接用新的threadlocal替换掉已经废弃的。另外每次在map中新建一个entry时(即没有和用过的或未清理的entry命中时),会调用cleanSomeSlots来遍历清理空间。此外,当Thread本身销毁时,这个map也一定被销毁了(map在Thread之内),这样内部所有绑定到该线程的ThreadLocal的Object Value因为没有引用继续保持,所以被销毁。

从上可以看出Java已经充分考虑了时间和空间的权衡,但是因为置为null的threadlocal对应的Object Value无法及时回收。map只有到达threshold时或添加entry时才做检查,不似gc是定时检查,不过我们可以手工轮询检查,显式调用map的remove方法,及时的清理废弃的threadlocal内存。需要说明的是,只要不往不用的threadlocal中放入大量数据,问题不大,毕竟还有回收的机制。

综上,废弃threadlocal占用的内存会在3中情况下清理:
1 thread结束,那么与之相关的threadlocal value会被清理
2 GC后,thread.threadlocals(map) threshold超过最大值时,会清理
3 GC后,thread.threadlocals(map) 添加新的Entry时,hash算法没有命中既有Entry时,会清理
那么何时会“内存泄露”?当Thread长时间不结束,存在大量废弃的ThreadLocal,而又不再添加新的ThreadLocal(或新添加的ThreadLocal恰好和一个废弃ThreadLocal在map中命中)时。
分享到:
评论
29 楼 honey_fansy 2014-12-03  
能讲讲为什么要用final修饰吗?还是说不一定?我看了好几段代码都加了
28 楼 heipacker 2014-07-24  
czpsailer 写道
为啥这么多人投隐藏,如果LZ那里说的不对可以指出,让大家学习下呀。我觉得至少LZ精神可嘉的。

建议隐藏
27 楼 yangyi 2014-05-15  
jackyin5918 写道


个人认为, 内部的ThreadLocalMap 还是 WeakHashMap ,当WeakHashMap的key为只有 弱引用(即,只有WeakHashMap 对象本身引用该key)时,包含该key的条目会被GC,这个没有问题. 但是内存泄漏问题,是因为ThreadLocal 对象被GC掉了,变成null了(假如ThreadLocal 对象 没有设置为Static的话),因为ThreadLocalMap 的是把ThreadLocalMap 当成key的,当key为null后,对应的value无法被回收,导致内存泄漏.
这也是 ThreadLocal 对象通常需要设置为Static的原因吧.
我之前说key为null说错了,其实应该是 只有弱引用指向 这个key
这是我的个人理解,如果不当,恳请指正.

No they both use the WeakRef concept but have different implementations. And this is on purpose for some perf reasons. Check the discussion bellow for more info:
https://community.oracle.com/thread/1143681?tstart=0
26 楼 jackyin5918 2014-05-14  
yangyi 写道
jackyin5918 写道
C_J 写道
- -! 你的意思是说:ThradLocal的内部类ThreadLocalMap这个map下的Entry在某些情况下存在内存泄漏吗?

  但这个Entry是弱引用,应该是被GC启动时无条件回收吧?而弱引用并不被JVM"标记",每次GC都回收的呀,还是我理解有误?


看了楼主的文章,也有类似的问题.

2 GC后,thread.threadlocals(map) threshold超过最大值时,会清理

关于这点有个疑问,因为 WeakHashMap 在key 为null时,只要GC,就会回收
key为null的entry. 为什么这里要等到 "threshold超过最大值时" 才 "会清理"
呢?


You are talking about WeakHashMap which is as what you described, but here we are saying an internal defined Map: ThreadLocalMap which uses a Weak ref instead of extends a WeakHashMap. Essentially just the ThreadLocal object here is Weak GC collectible, so that means another direct ref, the value, still holds after the time GC happens. Please correct me if not right.


个人认为, 内部的ThreadLocalMap 还是 WeakHashMap ,当WeakHashMap的key为只有 弱引用(即,只有WeakHashMap 对象本身引用该key)时,包含该key的条目会被GC,这个没有问题. 但是内存泄漏问题,是因为ThreadLocal 对象被GC掉了,变成null了(假如ThreadLocal 对象 没有设置为Static的话),因为ThreadLocalMap 的是把ThreadLocalMap 当成key的,当key为null后,对应的value无法被回收,导致内存泄漏.
这也是 ThreadLocal 对象通常需要设置为Static的原因吧.
我之前说key为null说错了,其实应该是 只有弱引用指向 这个key
这是我的个人理解,如果不当,恳请指正.
25 楼 yangyi 2014-05-14  
jackyin5918 写道
C_J 写道
- -! 你的意思是说:ThradLocal的内部类ThreadLocalMap这个map下的Entry在某些情况下存在内存泄漏吗?

  但这个Entry是弱引用,应该是被GC启动时无条件回收吧?而弱引用并不被JVM"标记",每次GC都回收的呀,还是我理解有误?


看了楼主的文章,也有类似的问题.

2 GC后,thread.threadlocals(map) threshold超过最大值时,会清理

关于这点有个疑问,因为 WeakHashMap 在key 为null时,只要GC,就会回收
key为null的entry. 为什么这里要等到 "threshold超过最大值时" 才 "会清理"
呢?


You are talking about WeakHashMap which is as what you described, but here we are saying an internal defined Map: ThreadLocalMap which uses a Weak ref instead of extends a WeakHashMap. Essentially just the ThreadLocal object here is Weak GC collectible, so that means another direct ref, the value, still holds after the time GC happens. Please correct me if not right.
24 楼 jackyin5918 2014-05-14  
C_J 写道
- -! 你的意思是说:ThradLocal的内部类ThreadLocalMap这个map下的Entry在某些情况下存在内存泄漏吗?

  但这个Entry是弱引用,应该是被GC启动时无条件回收吧?而弱引用并不被JVM"标记",每次GC都回收的呀,还是我理解有误?


看了楼主的文章,也有类似的问题.

2 GC后,thread.threadlocals(map) threshold超过最大值时,会清理

关于这点有个疑问,因为 WeakHashMap 在key 为null时,只要GC,就会回收
key为null的entry. 为什么这里要等到 "threshold超过最大值时" 才 "会清理"
呢?
23 楼 qianhd 2012-09-30  
如果ThreadLocalMap.Entry类覆写clear方法, 把value也置为null的话,
那这种多threadLocal+线程池所产生的内存泄漏方法就应该可以避免吧.

当然多个threadLocal,而且还不停的构造, 释放的设计就是有问题的.
22 楼 yangyi 2011-05-06  
引用
Technoboy 写道
引用

那么何时会“内存泄露”?当Thread长时间不结束,存在大量废弃的ThreadLocal,而又不再添加新的ThreadLocal(或新添加的ThreadLocal恰好和一个废弃ThreadLocal在map中命中)时。

我对这句话一直没太明白,什么情况下,Thread会长时间不结束呢?存在大量废弃的ThreadLocal,没有被回收,就与GC有关了。

这个是应用相关的和GC没有关系,比如线程池中的线程,core size范围内
引用

Entry是针对ThreadLocal弱引用的

弱引用的话,JVM在执行内存清理工作的时候,一般情况下会回收这段空间,但是也不一定。楼主的意思是说,如果存在内存的泄露,也是跟JVM有关系是吧?
那么,这个问题就与JVM采用什么类型的回收算法,什么时候进行回收不可达对象内存空间有关了。

和GC没有关系,是一种设计上的取舍
21 楼 Technoboy 2011-05-06  
引用

那么何时会“内存泄露”?当Thread长时间不结束,存在大量废弃的ThreadLocal,而又不再添加新的ThreadLocal(或新添加的ThreadLocal恰好和一个废弃ThreadLocal在map中命中)时。

我对这句话一直没太明白,什么情况下,Thread会长时间不结束呢?存在大量废弃的ThreadLocal,没有被回收,就与GC有关了。

引用

Entry是针对ThreadLocal弱引用的

弱引用的话,JVM在执行内存清理工作的时候,一般情况下会回收这段空间,但是也不一定。楼主的意思是说,如果存在内存的泄露,也是跟JVM有关系是吧?
那么,这个问题就与JVM采用什么类型的回收算法,什么时候进行回收不可达对象内存空间有关了。
20 楼 yangyi 2011-05-05  
<div class="quote_title">weifly 写道</div>
<div class="quote_div">
<div class="quote_title">yangyi 写道</div>
<div class="quote_div">那么何时会“内存泄露”?当Thread长时间不结束,存在大量废弃的ThreadLocal</div>
<p><br><br>存在大量废弃的ThreadLocal很有可能说明程序设计有问题吧?ThreadLocal一般是static的,只要类不被回收,那ThreadLocal也不会回收的啊,也就不存在废弃的说法了。<br><br>在finally中清空ThreadLocal中持有的对象引用就不会造成内存泄露</p>
<p> </p>
<pre name="code" class="java">public class MyClass {

private static final ThreadLocal threadLocal = new ThreadLocal();

private Object value;

public void myMethod() {
try {
threadLocal.set(value);
// 业务逻辑
} finally {
threadLocal.set(null);
}
}
}
</pre>
 
<p> </p>
</div>
<p>是的,这是一个怎么使用的问题,本文只是针对源码的分析,像ls说的这种使用方法是没有问题的,因为你显式的清空了threadlocal的值,文中说明的是自动回收</p>
19 楼 weifly 2011-05-05  
<div class="quote_title">yangyi 写道</div>
<div class="quote_div">那么何时会“内存泄露”?当Thread长时间不结束,存在大量废弃的ThreadLocal</div>
<p><br><br>存在大量废弃的ThreadLocal很有可能说明程序设计有问题吧?ThreadLocal一般是static的,只要类不被回收,那ThreadLocal也不会回收的啊,也就不存在废弃的说法了。<br><br>在finally中清空ThreadLocal中持有的对象引用就不会造成内存泄露</p>
<p> </p>
<pre name="code" class="java">public class MyClass {

private static final ThreadLocal threadLocal = new ThreadLocal();

private Object value;

public void myMethod() {
try {
threadLocal.set(value);
// 业务逻辑
} finally {
threadLocal.set(null);
}
}
}
</pre>
 
<p> </p>
18 楼 yangyi 2011-05-05  
redhat 写道
yangyi 写道
redhat 写道
引用
那么何时会“内存泄露”?当Thread长时间不结束,存在大量废弃的ThreadLocal,而又不再添加新的ThreadLocal(或新添加的ThreadLocal恰好和一个废弃ThreadLocal在map中命中)时。


楼主,你似懂非懂呀!你的意思是,如果存在强引用(或者是弱引用),那么在回收内存时不会清理弱引用,对吧。 呵呵,但是你想想,如果存在大量强引用的对象,你还想分配内存地址给你的对象,那会报“内存不够用”的异常,请您再发表这些言论时,为了避免误导大家,务必先做个实验验证!


ThreadLocal是弱引用的,但是map本身是强引用的,所以即使key被回收为null,value还在,另外,在很多threadlocal的应用中,value对象不再调用环境之外保留强引用,所以才会引起所谓“内存泄露”

看来要给你好好上一课了,map里的value,如果使用强引用,那么持续的往里面添加value,即使你外部程序对此value释放引用了,但是,你的map的key还没有,这样的map随着时间膨胀,不remove一些值的话,就会引起内存不够,这才是weakreferencemap使用的原因,当你程序的强引用指向此value断掉,那么应该map里自动断掉引用,回收此value,不管是weakmap还是softmap,都应该是对key的softreference或者weakrefrence的包装,试想下,如果程序运行过程中,你的value对象被回收了,那是多么悲哀的事情,程序不都是报nullpointexception了。
要知其然,才能明白所以然。

你说的都是对的,你仔细看我说的,和你说的矛盾吗?你为什么觉得要给我上一课,你懂的,别人不懂?
17 楼 redhat 2011-05-05  
yangyi 写道
redhat 写道
引用
那么何时会“内存泄露”?当Thread长时间不结束,存在大量废弃的ThreadLocal,而又不再添加新的ThreadLocal(或新添加的ThreadLocal恰好和一个废弃ThreadLocal在map中命中)时。


楼主,你似懂非懂呀!你的意思是,如果存在强引用(或者是弱引用),那么在回收内存时不会清理弱引用,对吧。 呵呵,但是你想想,如果存在大量强引用的对象,你还想分配内存地址给你的对象,那会报“内存不够用”的异常,请您再发表这些言论时,为了避免误导大家,务必先做个实验验证!


ThreadLocal是弱引用的,但是map本身是强引用的,所以即使key被回收为null,value还在,另外,在很多threadlocal的应用中,value对象不再调用环境之外保留强引用,所以才会引起所谓“内存泄露”

看来要给你好好上一课了,map里的value,如果使用强引用,那么持续的往里面添加value,即使你外部程序对此value释放引用了,但是,你的map的key还没有,这样的map随着时间膨胀,不remove一些值的话,就会引起内存不够,这才是weakreferencemap使用的原因,当你程序的强引用指向此value断掉,那么应该map里自动断掉引用,回收此value,不管是weakmap还是softmap,都应该是对key的softreference或者weakrefrence的包装,试想下,如果程序运行过程中,你的value对象被回收了,那是多么悲哀的事情,程序不都是报nullpointexception了。
要知其然,才能明白所以然。
16 楼 yangyi 2011-05-05  
redhat 写道
引用
那么何时会“内存泄露”?当Thread长时间不结束,存在大量废弃的ThreadLocal,而又不再添加新的ThreadLocal(或新添加的ThreadLocal恰好和一个废弃ThreadLocal在map中命中)时。


楼主,你似懂非懂呀!你的意思是,如果存在强引用(或者是弱引用),那么在回收内存时不会清理弱引用,对吧。 呵呵,但是你想想,如果存在大量强引用的对象,你还想分配内存地址给你的对象,那会报“内存不够用”的异常,请您再发表这些言论时,为了避免误导大家,务必先做个实验验证!


ThreadLocal是弱引用的,但是map本身是强引用的,所以即使key被回收为null,value还在,另外,在很多threadlocal的应用中,value对象不再调用环境之外保留强引用,所以才会引起所谓“内存泄露”
15 楼 redhat 2011-05-04  
末了,为了说明白一点,再补充上次回复说一句: 报“内存不够”的异常,一般是您设计的问题,为什么要设计很多线程不停地跑,而且不等它们跑完就要加新的线程进来占用内存?这和你有能容纳1T个对象能力的内存,你创建1T个对象,这些对象都有强引用指向,所以都不能回收,你还要创建1T个对象,那都会报内存不够的异常。
14 楼 redhat 2011-05-04  
引用
那么何时会“内存泄露”?当Thread长时间不结束,存在大量废弃的ThreadLocal,而又不再添加新的ThreadLocal(或新添加的ThreadLocal恰好和一个废弃ThreadLocal在map中命中)时。


楼主,你似懂非懂呀!你的意思是,如果存在强引用(或者是弱引用),那么在回收内存时不会清理弱引用,对吧。 呵呵,但是你想想,如果存在大量强引用的对象,你还想分配内存地址给你的对象,那会报“内存不够用”的异常,请您再发表这些言论时,为了避免误导大家,务必先做个实验验证!
13 楼 shellfj 2010-07-06  
欣赏lz看代码的精神和效果。
12 楼 beneo 2010-07-06  
threadlocal是一个weakhashmap

那么,threadlocal<you_instance>的you_instance又引用了threadlocal的话,会出现内存泄漏。

线程池的话,只会出现问题,线程不死,threadlocal里面的值不会被回收,存在一些风险,不会带来内存泄漏。
11 楼 rain2005 2010-07-05  
看来没有几个真正搞清楚的,请看源代码吧。

ThreadLocal一定要用static类型的,否则内存泄露。
10 楼 zhxing 2010-07-05  
Spring 中用到了大量的ThreadLocal,也未见什么大问题,是否lz 多虑了。

另外个人一个小小见解:
一般容器会有个线程池之类的,可以说是固定了线程数量了,线程被重用了,ThreadLocal 的值也会被覆盖,这时gc就可以清理了。。

相关推荐

    ThreadLocal 内存泄露的实例分析1

    问题背景在 Tomcat 中,下面的代码都在 webapp 内,会导致 WebappClassLoaderWebappClassLoader 泄漏,无法被回收。

    实例详解Java中ThreadLocal内存泄露

    一篇文章我们来分析一个Java中ThreadLocal内存泄露的案例。分析问题的过程比结果更重要,理论结合实际才能彻底分析出内存泄漏的原因。

    ThreadLocal内存泄露分析

    NULL 博文链接:https://liuinsect.iteye.com/blog/1827012

    04、导致JVM内存泄露的ThreadLocal详解-ev

    04、导致JVM内存泄露的ThreadLocal详解_ev04、导致JVM内存泄露的ThreadLocal详解_ev04、导致JVM内存泄露的ThreadLocal详解_ev04、导致JVM内存泄露的ThreadLocal详解_ev04、导致JVM内存泄露的ThreadLocal详解_ev04、...

    ThreadLocal中内存泄漏和数据丢失问题的问题浅析及解决方案.docx

    ThreadLocal中内存泄漏和数据丢失问题的问题浅析及解决方案.docx

    ThreadLocal原理及内存泄漏原因

    主要介绍了ThreadLocal原理及内存泄漏原因,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

    2、导致JVM内存泄露的ThreadLocal详解

    导致JVM内存泄露的ThreadLocal详解 为什么要有ThreadLocal ThreadLocal的使用 实现解析 引发的内存泄漏分析 错误使用ThreadLocal导致 线程不安全分析

    JVM的基础和调优【JMM 内存结构 GC OOM 性能调优 ThreadLocal】

    内存泄露:是指程序在申请内存后,无法释放已申请的内存空间就造成了内存泄露, 一次的内存泄露似乎不会有大的影响,但是内存泄露堆积的后果就是内存溢出 JMM 决定一个线程对共享变量的写入何时对另一个线程可见,...

    java中ThreadLocal详解

    详解java底层实现原理,ThreadLocal底层实现的数据结构,为什么不会导致内存泄露

    详解Java内存泄露的示例代码

    通过一个Demo来简要介绍下ThreadLocal和ClassLoader导致内存泄露最终OutOfMemory的场景。下面通过示例代码给大家分享Java内存泄露的相关知识,感兴趣的朋友一起看看吧

    如何用Java编写一段代码引发内存泄露

    文本来自StackOverflow问答网站的一个热门讨论:如何用Java编写一段会发生内存泄露的代码。  Q:刚才我参加了面试,面试官问我如何写出会发生内存泄露的Java代码。这个问题我一点思路都没有,好?。  A1:通过...

    ThreadLocal,你真的了解吗?

    6)Thread会产生内存泄漏吗? 在学习ThreadLocal之前,先了解一下java中的四种引用,大厂面试的时候,面试官一般都是先问四种引用,然后过渡到ThreadLocal。 2.Java中的引用类型 从Java SE2开始,就提供了四种类型的...

    java并发编程面试题

    java并发编程 基础知识,守护线程与线程, 并行和并发有什么区别? 什么是上下文切换? 线程和进程区别 什么是线程和进程? 创建线程有哪几种方式?,如何避免线程死锁 ...ThreadLocal内存泄漏分析与

    【2018最新最详细】并发多线程教程

    18.一篇文章,从源码深入详解ThreadLocal内存泄漏问题 19.并发容器之BlockingQueue 20.并发容器之ArrayBlockingQueue和LinkedBlockingQueue实现原理详解 21.线程池ThreadPoolExecutor实现原理 22.线程池之...

    Java初级、中级、高级面试题及答案

    事务\事务隔离级别\Mysql默认隔离级别\串行化\存储引擎Innodb\Myisam\Inodb锁机制\MVCC\B树索引\...ThreadLocal\内存泄漏\序列化和反序列化\ArrayList\HashMap\正则表达式\设计模式\linux指令\多线程\Redis\三大排序\

    Java并发编程学习笔记

    本文档主要内容如下: 1、线程安全和锁 Synchronized 底层实现原理 2、可重入锁与 Synchronized 的...9、ThreadLocal 为什么会内存泄漏 10、Volatile底层实现原理 11、AQS源码分析 12、CAS原理分析和使用场景 13、.....

    springboot_mongodb

    2 ThreadLocal内部的ThreadLocalMap键为弱引用,会有内存泄漏的风险。 3适用于无状态,副本变量独立后不影响业务逻辑的高并发场景。如果如果业务逻辑强依赖于副本变量,则不适合使用ThreadLocal解决,需要另寻解决...

    redar:Java Swift Knife 项目.. - 线程、Quartz、Spring、Spring Integration、Apache Mina、Python

    多线程示例####1.1 ThreadLocals + ThreadPools 该模块通过确保在 Runnable 执行结束时删除 ThreadLocal 上下文来测试内存泄漏保护####1.2。 分叉和加入本模块用于测试 Fork And Join ####1.3。 通过 RMI 使用信号...

Global site tag (gtag.js) - Google Analytics