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

时间: 2024-10-15 00:47:27

ThreadLocal是否会引发内存泄露的分析 good的相关文章

(转)专项:Android 内存泄露实践分析

今天看到一篇关于Android 内存泄露实践分析的文章,感觉不错,讲的还算详细,mark到这里. 原文发表于:Testerhome: 作者:ycwdaaaa ;  原文链接:https://testerhome.com/topics/5822 定义 ​内存泄漏也称作"存储渗漏",用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元.直到程序结束.(其实说白了就是该内存空间使用完毕之后未回收)即所谓内存泄漏.  内存泄漏形象的比喻是"操作系统可提供给

java程序内存不下降,但是也没有内存泄露怎么分析?

问题描述 java程序内存不下降,但是也没有内存泄露怎么分析? 我们用java开发的程序.运行过程中使用任务管理器观察内存可以达到700M.使用java自带的jvisualvm.exe查看内存申请了200多用了100M,请问剩下的500M去哪里了.用什么方法可以找到这些内存的去向.

专项:Android 内存泄露实践分析

定义 内存泄漏也称作"存储渗漏",用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元.直到程序结束.(其实说白了就是该内存空间使用完毕之后未回收)即所谓内存泄漏.   内存泄漏形象的比喻是"操作系统可提供给所有进程的存储空间正在被某个进程榨干",最终结果是程序运行时间越长,占用存储空间越来越多,最终用尽全部存储空间,整个系统崩溃.所以"内存泄漏"是从操作系统的角度来看的.这里的存储空间并不是指物理内存,而是指虚拟内存

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

Q:刚才我参加了面试,面试官问我如何写出会发生内存泄露的Java代码.这个问题我一点思路都没有,好囧. A1:通过以下步骤可以很容易产生内存泄露(程序代码不能访问到某些对象,但是它们仍然保存在内存中): 应用程序创建一个长时间运行的线程(或者使用线程池,会更快地发生内存泄露). 线程通过某个类加载器(可以自定义)加载一个类. 该类分配了大块内存(比如new byte[1000000]),在某个静态变量存储一个强引用,然后在ThreadLocal中存储它自身的引用.分配额外的内存new byte[

ThreadLocal可能引起的内存泄露(转)

  小结ThreadLocal是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题.在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性.http://sunnylocus.iteye.com/blog/801949 ThreaLocal是通过数据隔离机制来解决线程同步问题的 如果涉及到多个线程需要共享同一数据 比如计数器 此时在面临线程同步问题时就只能采取sync

Flex应用内存泄露的分析与诊断

引言 Flex 采用 ActionScript 语言作为脚本语言,编译后的二进制代码在 FlashPlayer 虚拟机 AVM(Actionscript Virtual Machine)中运行.和 Java 语言类似, AVM 中也有一个垃圾收集器(Garbage Collection),对于不用的对象,隔一段时间会进行 收集并销毁,释放内存.和 C++ 语言相比,程序员不需要时刻关注内存的分配和释放,大大 减轻了负担.但是垃圾收集器不能从根本上解决 Flex 内存泄露的问题,这得从 Flash

Java内存泄露问题分析

很多人在谈论内存泄露问题,当然对于c/c++来说,这个应该是老掉牙的问题,但是很多Java人员也越来越多得讨论这个问题,我这里写个小结,希望对大家有一定的参考价值. 内存泄漏的慨念 1.c/c++是程序员自己管理内存,Java内存是由GC自动回收的. 我虽然不是很熟悉C++,不过这个应该没有犯常识性错误吧. 2.什么是内存泄露? 内存泄露是指系统中存在无法回收的内存,有时候会造成内存不足或系统崩溃. 在C/C++中分配了内存不释放的情况就是内存泄露. 3.Java存在内存泄露 我们必须先承认这个

关于java内存泄露求大神帮忙分析

问题描述 关于java内存泄露求大神帮忙分析 用MAT进行分析但是有点看不懂求大神帮忙分析一下 解决方案 Java内存泄露和分析Java内存泄露分析Java内存泄露问题分析 解决方案二: Details 看看 解决方案三: 你看看你有没有写入到内存里很大的文件 解决方案四: 这是今天重新定位问题的明细图片 解决方案五: 这是相关数据的图片

PHP对象相互引用的内存溢出实例分析_php技巧

通常来说使用脚本语言最大的好处之一就是可利用其拥有的自动垃圾回收机制来释放内存.你不需要在使用完变量后做任何释放内存的处理,因为这些PHP会帮你完成. 当然,我们可以按自己的意愿调用 unset() 函数来释放内存,但通常不需要这么做. 不过在PHP里,至少有一种情况内存不会得到自动释放,即便是手动调用 unset().详情可考PHP官网关于内存泄露的分析:http://bugs.php.net/bug.php?id=33595. 问题症状如下: 如果两个对象之间存在着相互引用的关系,如"父对象