JDK源码系列:讲清楚ThreadLocal内存泄漏的来龙去脉

46f3c82a19feb4d5bf008e3bdd445799.jpeg

一、ThreadLocal\Thread\ThreadLocalMap的内存模型

5e579f0101c23e48018a1eb9e8f25f51.png

在研究过程中还思考了几个小问题:

1、线程结束后Thread对象会被释放吗?

Thread对象是线程状态的一个影像,线程状态的变化会反应到Thread.getState()方法上。

线程结束后会将Thread.state置为“TERMINATED”,但是Thread内存对象的回收还是和其它Java对象一样的规则。

线程在未结束之前,Thread对象会一直存在。

2、线程结束后Thread对象中的threadLocals一定会被释放吗?

是的,通过实验发现,线程结束后 Thread对象上的threadLocals一定会被置为null

二、内存是怎么泄漏的?

ThreadLocalMap.Entry持有ThreadLocal对象的弱引用,从而不会影响ThreadLocal对象的GC,对value的引用是强引用,只有当ThreadLocalMap的底层数组不再持有这个Entry时,该Entry才会被GC。也就是说,如果ThreadLocalMap如果不做特殊处理的话,那么即使是ThreadLocal实例都被GC了,但是它们对应的Entry依旧无法被GC,导致实际使用value对象也无法被GC,只是这些Entry引用的ThreadLocal变成null了,这个问题其实就是所谓的内存泄露。

1、Java线程退出时黄色区域会被GC回收,Thread对象按普通Java对象规则回收

cc54f1600b1f725423472fbd81ec25ec.png

2、考虑“线程池”的情况,Java线程不会退出,黄色区域就不会被回收

fff217f5b3dad83d33cbcc7e7e2ba68b.png

3、由于key与TheadLocal对象是“弱引用关系”,也就是如果ThreadLocal对象在没有“强引用”的情况下,下一次GC就会被回收,这时key的指向就会变成null,从而导致value无法被再次访问,但是又不释放内存(Entry节点没有被回收,value就不会被回收),这就导致了内存泄漏

9ab6c84f915c4f050d1f661e78533f91.png

不主动调用threadlocal的remove方法和threadlocal强引用丢失是造成 内存泄漏的主要原因

24566710bb317f80dd15b523834e4dc6.png

三、ThreadLocalMap做了哪些努力解决泄漏问题?

为了解决这个内存泄露问题(也就是key为null的那些Entry),ThreadLocalMap在扩容和线性探测等操作中,如果发现了持有的thread local已经被GC的Entry,那么就会将这个Entry置为null,使得这个Entry可以被GC,但是即使这样依然无法完全保证出现问题的 entry都能及时的被清理,这个残留的问题就是内存泄露问题。

这个内存泄露问题一般存在于线程池的场景下,因为如果线程本身被销毁,那么thread local map也会销毁,也不存在什么泄露问题。

为了解决这个内存泄露问题,我们在使用到threadlocal时,如果我们不再需要它时,那么就要手动进行remove操作,使得对应的Entry可以被GC。

同时ThreadLocalMap在多种场景中会主动清理坏掉的Entry(key为null的Entry),清理的场景与算法另起一篇文章说明

四、开发者应该采取哪些措施预防泄漏?

1)使用完毕后一定要调用 

 threadLocal.remove()方法释放线程上绑定的资源

2)threadLocal一般声明为static类型的全局变量,而不是局部变量

3)一般用来传递隐式公共参数,所以一般赋值时机是在第一个前置过滤器或者拦截器中,释放时机一般是在最后一个后置过滤器或者或者拦截器中

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吕哥架构

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值