论坛首页 Java企业应用论坛

关于ThreadLocal的内存泄露

浏览 15776 次
该帖已经被评为良好帖
作者 正文
   发表时间:2010-07-02   最后修改:2010-07-09
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中命中)时。
   发表时间:2010-07-02  
本文基于对源代码的理解,如有错误请指正
0 请登录后投票
   发表时间:2010-07-03  
没看过源代码,理论上是这样吧。
Thread有长时间不结束的情况吗?除非定时器,但是一个应用的定时器最多也是有限的几
个,如果一个应用有长时间不结束的Thread,就要检查代码写的是否有问题了。
另外,一个应用处理高并发的时候都会建立线程池,控制线程的最大值。
我觉得还是可以使用Threalocal来做应用开发的。
0 请登录后投票
   发表时间:2010-07-03  
yumcn.com 写道
没看过源代码,理论上是这样吧。
Thread有长时间不结束的情况吗?除非定时器,但是一个应用的定时器最多也是有限的几
个,如果一个应用有长时间不结束的Thread,就要检查代码写的是否有问题了。
另外,一个应用处理高并发的时候都会建立线程池,控制线程的最大值。
我觉得还是可以使用Threalocal来做应用开发的。

恩,因为一直有人说ThreadLocal存在内存泄露问题,我才探究了一下,事实上问题不大。所谓内存泄露更多是出于一种性能的考量
0 请登录后投票
   发表时间:2010-07-04  
为啥这么多人投隐藏,如果LZ那里说的不对可以指出,让大家学习下呀。我觉得至少LZ精神可嘉的。
0 请登录后投票
   发表时间:2010-07-04  
赞楼主的精神先,但个人有个小建议, 在解释文字中,可以适量加一些代码,或者使文章更有可读性。
0 请登录后投票
   发表时间:2010-07-04  
static  ThreadLocal
0 请登录后投票
   发表时间:2010-07-04   最后修改:2010-07-04
- -! 你的意思是说:ThradLocal的内部类ThreadLocalMap这个map下的Entry在某些情况下存在内存泄漏吗?

  但这个Entry是弱引用,应该是被GC启动时无条件回收吧?而弱引用并不被JVM"标记",每次GC都回收的呀,还是我理解有误?
0 请登录后投票
   发表时间:2010-07-05  
要善于和je的恶势力作斗争,精华了。
0 请登录后投票
   发表时间:2010-07-05  
yumcn.com 写道
没看过源代码,理论上是这样吧。
Thread有长时间不结束的情况吗?除非定时器,但是一个应用的定时器最多也是有限的几
个,如果一个应用有长时间不结束的Thread,就要检查代码写的是否有问题了。
另外,一个应用处理高并发的时候都会建立线程池,控制线程的最大值。
我觉得还是可以使用Threalocal来做应用开发的。


用ThreadLocal来做开发当然没问题。说白了,SUN引入ThreadLocal无非就是在解决变量共享冲突问题上用空间来换时间,所以处理不好有泄漏应该算是合理的。
我在前一阵刚读过ThreadLocal的源代码,对比LZ的分析很惭愧啊,看来应该多学习LZ的精神啊。

引用

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


如果当ThreadLocal遇到线程池,那么发生泄漏的几率会大一些呢?
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics