`
liuInsect
  • 浏览: 133356 次
  • 性别: 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  
我有点晕,刚开始说有内存泄露,后面又说没有,到底是有还是没有?
我看你开始说的觉得确实有内存泄露啊。

相关推荐

    ThreadLocal 内存泄露的实例分析1

    但这里,`ThreadLocal` 的设计使得其内部的引用(即使是最弱的引用)在类加载器试图卸载时依然存在,这就形成了一个内存泄漏点。 解决这个问题的关键在于,我们需要确保在不再需要 `ThreadLocal` 变量时,及时清除...

    ThreadLocal_ThreadLocal源码分析_

    Entry继承自WeakReference<ThreadLocal>,这意味着ThreadLocal对象如果不再被引用,可以被垃圾收集器回收,避免内存泄漏。然而,这种设计也存在一个问题:如果ThreadLocal没有被外部引用,但其对应的Entry还在Map中...

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

    ### 导致JVM内存泄露的ThreadLocal详解 #### 一、为什么要有ThreadLocal 在多线程编程中,为了避免线程间的数据竞争和保证线程安全性,常常需要使用同步机制如`synchronized`来控制线程对共享资源的访问。然而,...

    ThreadLocal_ThreadLocal源码分析_源码.zip

    4. **内存泄漏问题**:尽管Entry使用了弱引用,但当线程不结束而ThreadLocal对象被回收时,Entry中的value仍然存在,可能导致内存泄漏。因此,使用完ThreadLocal后,最好显式调用`remove()`方法。 5. **线程局部...

    java并发编程面试题

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

    Java系统中内存泄漏测试方法的研究

    4. ThreadLocal:如果线程局部变量未被正确清理,可能会导致内存泄漏。 5. 注册监听器未取消:注册的监听器如果没有在适当的时候解除注册,会持续持有对对象的引用。 四、内存泄漏的检测方法 1. **手动代码审查**...

    ThreadLocal分析

    过度依赖ThreadLocal可能导致代码难以理解和维护,且可能导致内存泄漏。在设计和使用ThreadLocal时,需要谨慎评估其必要性和潜在风险。 在分析ThreadLocal源码时,可以了解到它如何在内部实现线程隔离,以及...

    关于java内存泄漏

    1. **工具辅助**:使用诸如VisualVM、JProfiler等工具来监控和分析应用程序的内存使用情况,找出潜在的内存泄漏源。 2. **代码审查**:定期进行代码审查,尤其是关注静态集合类、监听器、内部类以及ThreadLocal的...

    threadLocal

    3. 内存管理:了解Java的内存模型和垃圾回收机制,才能理解ThreadLocal的内存泄漏风险和弱引用的作用。 4. HTTP相关:虽然题目中没有直接涉及,但HTTPClient是一个常见的网络通信工具,经常和ThreadLocal结合使用,...

    正确理解ThreadLocal.pdf

    然而,如果线程持续运行但不再使用某些`ThreadLocal`变量,应显式调用`remove()`方法释放这些变量,避免内存泄漏。 #### 五、ThreadLocal的应用场景 1. **数据库连接管理**:如上文的Hibernate示例,通过`...

    一次使用Eclipse Memory Analyzer分析Tomcat内存溢出

    - **Leak Suspects**:提供自动分析内存泄露的可能原因。 ##### 3.3 具体案例分析 假设在生产环境中,一个项目运行三周左右会出现内存溢出异常。JDK 使用的是 64 位版本,配置参数为 `-Xmx3078M -Xms3078M -XX:...

    java并发包源码分析(3)ThreadLocal

    - 执行remove操作,之后通过调试工具检查内存中的对象是否被回收,以此来验证ThreadLocal的内存泄漏问题。 ThreadLocal是一个功能强大但又需要仔细使用的工具,掌握其原理和正确使用方法对于编写高性能和高可靠性的...

    ThreadLocal的用处

    - 清理:如果不手动调用`remove()`方法,线程结束后,ThreadLocal变量不会自动清除,可能导致内存泄漏。因此,使用完ThreadLocal变量后,最好显式调用`remove()`。 4. **避免内存泄漏**: 由于ThreadLocalMap使用...

    ThreadLocal的原理,源码深度分析及使用.docx

    * 内存泄露:如果 ThreadLocal 对象不被正确释放,将会导致内存泄露。 * 性能问题: ThreadLocal 的实现机制可能会带来一些性能问题,例如哈希表的查找和更新操作。 ThreadLocal 是 Java 语言中的一种机制,用于...

    ThreadLocal 线程本地变量 及 源码分析.rar_开发_设计

    如果线程长时间运行或应用中存在大量线程复用,不及时清理ThreadLocal可能导致内存泄漏。 - 应在不再需要ThreadLocal时调用`remove()`方法来清除,以避免内存资源浪费。 3. **ThreadLocal的内部实现** - ...

    JDK的ThreadLocal理解(一)使用和测试

    因此,如果不再使用ThreadLocal,建议显式调用`remove()`方法,以避免内存泄漏。 ### 3. ThreadLocal的工作原理 ThreadLocal的内部维护了一个ThreadLocalMap,它是基于弱引用的HashMap。每个线程都有自己的...

    简单分析Java线程编程中ThreadLocal类的使用共

    4. **清理ThreadLocal**:由于ThreadLocal变量在内存中会占用资源,当线程结束或者不再需要这些变量时,应当使用`remove()`方法进行清理,防止内存泄漏。 ```java threadLocalVar.remove(); ``` ThreadLocal的...

    ThreadLocal

    - 内存泄漏:如果不再使用ThreadLocal,但没有调用`remove()`,那么在长时间运行的应用中,这些线程局部变量可能会导致内存泄漏。因为线程结束后,其ThreadLocal的引用并不会自动清除。 - 不适用于跨线程通信:...

Global site tag (gtag.js) - Google Analytics