`
liuInsect
  • 浏览: 132017 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

ThreadLocal内存泄露分析

阅读更多
为了更好的提供文章,我已经将博客迁移到了自建的博客网站上,我将更多的从源码分析的角度入手,为大家带来更多的深度文章,请大家继续关注我~!  博客地址:www.liuinsect.com
_______________________________________________________________________________
 
这篇文章,主要解决一下疑惑:
1. ThreadLocal.ThreadLocalMap中提到的弱引用,弱引用究竟会不会被回收?
2. 弱引用什么情况下回收?
3. JAVA的ThreadLocal和在什么情况下会内存泄露?
 
带着这些疑问,自己模拟了一下ThreadLocal.ThreadLocalMap的结构,先展示下自己涉及的结构:

自己实现一个simple的ThreadLocalMap,里面用一个entry用来存放由自己模拟的ThreadLocal调用set方法set进去的值。
并且和JDK的ThreadLocalMap一样里面Entry对象的key用weakReference封装。
 
Main方法如下:

  
 
设置运行参数:
-verbose:gc  
 
看输出结果:
 这里我已经模拟出了内存泄露的问题,可以看到FULL GC以后,内存还是被占用,且仔细观察可以看到,这个map中的Key已经有为null了。换句话说你通过Key已经不能获取到value了,当然map.get(null)也是可以的,不过
JAVA里的ThreadLocal不会这么去做,因为Map中key==null的元素可能不唯一。
从我的Main方法中可以看到,我有th=null的操作,但是还是有内存泄露,原因稍后分析。但有一点可以确定:th=null在这里不能如我们想象的将ThreadLocal th 的引用释放掉后,里面的key,value对象也释放,可能会有疑问我这里持有了ThreadLocalMap的引用tm所以不会回收,但实际上,手动设置JAVA的ThreadLocal为null时,当前线程任然持有ThreadLocalMap的引用,所以不会回收我这里和JAVA是类似的
 
回到刚开始提出的3个问题,一一解答:
1. ThreadLocal.ThreadLocalMap中提到的弱引用,弱引用究竟会不会被回收?
     会被回收,如上图所示。key 已经有null的情况了。第一个Key不为null,原因在第二点。在经历过FULL GC后 所有的key都被回收了。
2. 弱引用什么情况下回收?
     弱引用在GC(包括MinitorGC和Full GC)时,被扫描到就会被回收,但是有一个前提,该弱引用在外部没有被引用到(这个时候外部的引用等于强引用)。
     换句话说,如果我main方法中持有一个key的引用,哪怕他put进Map后被设置为弱引用的,也不会被回收。见下图:

 

GC 日志:

 
     
3. JAVA的ThreadLocal和在什么情况下会内存泄露?
   答案是不会,原因如下图,在我们调用ThreadLocal.set()的时候,会做一个将Key== null 的元素清理掉的工作,具体做法是:
     第一步:ThreadLocalMap 拿threadLocalHashCode与长度减一相与,求出哈希表的位置下图中的 i 。
     第二步:编列Entry,如果找到key相等的,覆盖原值! 或者找到key==null的,将值set进去,并且将遍历时路过的key==null的元素和他的value都置为null,,释放内存。
     第三步:最后一个if条件时,做rehash的动作,即:将Entry里的元素重新计算一下Hash值,放到合适的位置去,猜想是为了加快下次访问的速度。


 
总结:
     从这里看出,JAVA的ThreadLocal对Key使用到了弱引用,但是为了保证不再内存泄露,在每次set.get的时候主动对key==null的entry做遍历回收。
     虽然不会造成内存泄露,但是因为只有在每次set,get的时候才会对entry做key==null的判断,从而释放内存,所以可能使大对象在内存中存活很长一段时间,从而占用内存。
     所以,我们在使用完ThreadLocal里的对象后最好能手动remove一下,或者至少调用下ThreadLocal.set(null)。
     值得注意的是ThreadLocal中的key是当前当前ThreadLocal自己,就像上面模拟的外部持有强引用的情况,ThreadLocal.ThreadLocalMap中的key==null情况很少出现,因为,大部分情况ThreadLocal是以单例模式一直存在的。

 

  • 大小: 18.7 KB
  • 大小: 15.9 KB
  • 大小: 26.8 KB
  • 大小: 26.4 KB
  • 大小: 39.3 KB
  • 大小: 26.2 KB
2
1
分享到:
评论
8 楼 hxwabc 2016-03-03  
其实没有必要这么麻烦,只要ThreadLocal是static就不会有这些问题了。
7 楼 heipacker 2014-04-28  
楼主麻烦把内存泄露定义下
6 楼 liuInsect 2014-02-12  
ssrwf 写道
楼主根本没看过ThreadLocal的源码,ThreadLocal.ThreadLocalMap有这个成员吗?
ThreadLocal调用的是当前线程Thread.ThreadLocalMap,key是ThreadLocal对象,value是你要set的值,生命周期取决于当前线程。


正解,不过,我说明下,我所说的ThreadLocal.ThreadLocalMap 指的是每个线程自己的map,我表述确实不是非常的明确,但是我相信 如果你仔细看完这篇文章 能明白这个意思。
5 楼 ssrwf 2014-02-11  
楼主根本没看过ThreadLocal的源码,ThreadLocal.ThreadLocalMap有这个成员吗?
ThreadLocal调用的是当前线程Thread.ThreadLocalMap,key是ThreadLocal对象,value是你要set的值,生命周期取决于当前线程。
4 楼 liuInsect 2013-09-23  
3GQQ2012 写道
不错,楼主理解的很到位。


谢谢
3 楼 3GQQ2012 2013-09-17  
不错,楼主理解的很到位。
2 楼 liuInsect 2013-03-19  
huangyunbin 写道
我有点晕,刚开始说有内存泄露,后面又说没有,到底是有还是没有?
我看你开始说的觉得确实有内存泄露啊。


开始 内存泄露是我自己模拟的时候模拟出来的 你可以下载我的源码看看。
而JAVA是考虑了这种情况的,所以不会出现内存泄露,有疑问 继续交流哈          
1 楼 huangyunbin 2013-03-18  
我有点晕,刚开始说有内存泄露,后面又说没有,到底是有还是没有?
我看你开始说的觉得确实有内存泄露啊。

相关推荐

Global site tag (gtag.js) - Google Analytics