• ThreadLocal是否会引发内存泄露的分析 good


    这篇文章,主要解决一下疑惑:
    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
     
    http://liuinsect.iteye.com/blog/1827012
  • 相关阅读:
    数据结构与算法部分习题题解
    Codeforces Round #372 +#373 部分题解
    KMP算法的正确性证明及一个小优化
    后记
    BZOJ 4089:[Sdoi2015]graft(SDOI 2015 Round 2 Day 2)
    BZOJ 4085:[Sdoi2015]bigyration(SDOI 2015 round 2 Day 1)
    使用 async await 封装微信小程序HTTP请求
    mongo创建数据库和用户
    把实体bean对象转换成DBObject工具类
    Trident整合MongoDB
  • 原文地址:https://www.cnblogs.com/softidea/p/4819866.html
Copyright © 2020-2023  润新知