ThreadLocal内存泄漏问题解决方案
如果说ThreadLocal的话,那肯定就会涉及到内存泄漏,为啥嘞
因为吧啦吧啦~
ThreadLocal解决了什么问题呢?
它是为了解决对象不能被多线程共享访问的问题,通过threadLocal.set()方法将对象实例保存在每个线程自己所拥有的threadLocalMap中,这样的话每个线程都使用自己的对象实例,彼此不会影响从而达到了隔离的作用,这样就解决了对象在被共享访问时带来的线程安全问题。
啥意思呢?打个比方,现在公司所有人都要填写一个表格,但是只有一支笔,这个时候就只能上个人用完了之后,下个人才可以使用,为了保证"笔"这个资源的可用性,只需要保证在接下来每个人的获取顺序就可以了,这就是lock的作用,当这支笔被别人用的时候,我就加lock,你来了那就进入队列排队等待获取资源(非公平方式那就另外说了),这支笔用完之后就释放lock,然后按照顺序给下个人使用。
但是完全可以一个人一支笔对不对,这样的话,你填写你的表格,我填写我的表格,咱俩谁都不耽搁谁。这就是ThreadLocal在做的事情,因为每个Thread都有一个副本,就不存在资源竞争,所以也就不需要加锁,这不就是拿空间去换了时间嘛!
在开始之前,咱们先把Thread,ThreadLocal,ThreadLocalMap的关系捋一捋:
可以看到,在Thread中持有一个ThreadLocalMap,ThreadLocalMap又是由Entry来组成的,在Entry里面有ThreadLocal和value
ThreadLocal为啥动不动就内存泄漏呢?
在这里先给个解释,后面咱们再详细分析:
首先是因为ThreadLocal是基于ThreadLocalMap实现的,其中ThreadLocalMap的Entry继承了WeakReference,而Entry对象中的key使用了WeakReference封装,也就是说,Entry中的key是一个弱引用类型,对于弱引用来说,它只能存活到下次GC之前
如果此时一个线程调用了ThreadLocalMap的set设置变量,当前的ThreadLocalMap就会新增一条记录,但由于发生了一次垃圾回收,这样就会造成一个结果:key值被回收掉了,但是value值还在内存中,而且如果线程一直存在的话,那么它的value值就会一直存在
这样被垃圾回收掉的key就会一直存在一条引用链:Thread->ThreadLocalMap->Entry->Value:
就是因为这条引用链的存在,就会导致如果Thread还在运行,那么Entry不会被回收,进而value也不会被回收掉,但是Entry里面的key值已经被回收掉了
这只是一个线程,如果再来一个线程,又来一个线程…多了之后就会造成内存泄漏
知道是怎么造成内存泄漏之后,接下来要做的事情就好说了,不是因为value值没有被回收掉所以才会导致内存泄露的嘛
那使用完key值之后,将value值通过remove方法remove掉,这样的话内存中就不会有value值了,也就防止了内存泄漏嘛
ThreadLocal是基于ThreadLocalMap实现的?
OK,上面的内容讲完了,接下来一一来看
首先,你怎么知道ThreadLocal是基于ThreadLocalMap实现的呢?
从源码知道的~
在源码中能够看到下面这几行代码:
publicclassThreadLocal{ staticclassThreadLocalMap{ staticclassEntryextendsWeakReference >{ /**ThevalueassociatedwiththisThreadLocal.*/ Objectvalue; Entry(ThreadLocal>k,Objectv){ super(k); value=v; } } } }}}}
代码中说的很清楚了,在ThreadLocal内部维护着ThreadLocalMap,而它的Entry则继承自WeakReference的ThreadLocal ,其中Entry的k为ThreadLocal,v为Object,在调用super(k)时就将ThreadLocal实例包装成了一个WeakReference
强弱引用这块内容阿粉就直接放一个表格吧:
引用类型 | 功能特点 |
---|---|
强引用(StrongReference) | 被强引用关联的对象永远不会被垃圾回收器回收掉 |
软引用(SoftReference) | 软引用关联的对象,只有当系统将要发生内存溢出时,才会去回收软引用引用的对象 |
弱引用(WeakReference) | 只被弱引用关联的对象,只要发生垃圾收集事件,就会被回收 |
虚引用(PhantomReference) | 被虚引用关联的对象的唯一作用是能在这个对象被回收器回收时收到一个系统通知 |
从表格中应该能够看出来,弱引用的对象只要发生垃圾收集事件,就会被回收
所以弱引用的存活时间也就是下次GC之前了
在这里阿粉就有个问题想问问了:为什么ThreadLocal采用弱引用,而不是强引用嘞?
在ThreadLocalMap上面有些注释,我在这里摘录一部分,或许可以从中窥探一二:
Tohelpdealwithverylargeandlong-livedusages,thehashtableentriesuseWeakReferencesforkeys
翻译一下就是:(虽然我英语不是很好
为了解决非常大且长期使用的问题,哈希表使用了弱引用的key
假设,假设,ThreadLocal使用的是强引用,会怎样呢?
如果是强引用的话,在表格中也能够看出来,被强引用关联的对象,永远都不会被垃圾回收器回收掉
如果引用的ThreadLocal对象被回收了,但是ThreadLocalMap还持有对ThreadLocal的强引用,如果没有remove的话,在GC时进行可达性分析,ThreadLocal依然可达,这样就不会对ThreadLocal进行回收,但是我们期望的是引用的ThreadLocal对象被回收,这样不就达不到目的了嘛
使用弱引用的话,虽然会出现内存泄漏的问题,但是在ThreadLocal生命周期里面,都有对key值为null时进行回收的处理操作
所以,使用弱引用的话,可以在ThreadLocal生命周期中尽可能保证不出现内存泄漏的问题
啥?在ThreadLcoal生命周期里面,都有对key值为null时进行回收的处理操作?有证据么?
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。