threadlocal内存泄露_ThreadLocal与内存泄漏

820e08a05012fa860bfa53fd6b4d834b.png

阅读本文前,需要先了解ThreadLocal的原理。
我们将从下面这些问题开始,寻找ThreadLocal中内存泄漏的罪魁祸首。

一、ThreadLocal,Thread,ThreadLocalMap,Entry<k,v>之间的关系?

先来看看Thread源码

0de7182f8a24bd10bddc90e16c9b22e6.png

可以发现,每一个Thread中维护了一个ThreadLocalMap成员变量(也称threadLocals)。

再来看看ThreadLocal中的源码

ff86f73b42bd52501db0cfff4651c245.png

不难看出ThreadLocalMap类是ThreadLocal类的静态内部类,而Entry是ThreadLocalMap的静态内部类,key是ThreadLocal(声明为弱引用),value是Object,也就是我们要存的值。

二、ThreadLocal在预防内存泄漏方面,做了哪些努力?

Thread中维护了ThreadLocalMap,所以ThreadLocalMap的生命周期和Thread(当前线程)一样长。使用不当就可能会导致内存泄漏问题。但是,在ThreadLocal中,进行get,set操作的时候会清除Map里所有key为null的value。

三、ThreadLocal的实现原理?

aa8ce4c010fb45bf91b017e62c4b60dd.png

ThreadLocal自身并不储存值,而是作为一个key来让线程从ThreadLocal获取value。Entry是中的key是弱引用,所以jvm在垃圾回收时如果外部没有强引用来引用它,ThreadLocal必然会被回收。但是,作为ThreadLocalMap的key,ThreadLocal被回收后,ThreadLocalMap就会存在null,但value不为null的Entry。若当前线程一直不结束,可能是作为线程池中的一员,线程结束后不被销毁,或者分配(当前线程又创建了ThreadLocal对象)使用了又不再调用get/set方法,就可能引发内存泄漏。其次,就算线程结束了,操作系统在回收线程或进程的时候不是一定杀死线程或进程的,在繁忙的时候,只会清除线程或进程数据的操作,重复使用线程或进程(线程id可能不变导致内存泄漏)。因此,key弱引用并不是导致内存泄漏的原因,而是因为ThreadLocalMap的生命周期与当前线程一样长,并且没有手动删除对应key。

那么,为什么要将Entry中的key设为弱引用?相反,设置为弱引用的key能预防大多数内存泄漏的情况。如果key 使用强引用,引用的ThreadLocal的对象被回收了,但是ThreadLocalMap还持有ThreadLocal的强引用,如果没有手动删除,ThreadLocal不会被回收,导致Entry内存泄漏。如果key为弱引用,引用的ThreadLocal的对象被回收了,由于ThreadLocalMap持有ThreadLocal的弱引用,即使没有手动删除,ThreadLocal也会被回收。value在下一次ThreadLocalMap调用set,get,remove的时候会被清除。

四、如何避免上述弱引用引发的内存泄漏?

在使用完ThreadLocal时,及时调用它的的remove方法清除数据。

总而言之,如果开发者希望将类的某个静态变量与线程状态关联,可以考虑使用ThreadLocal。ThreadLocal的设计本身就是为了能够在当前线程中有属于自己的变量,并不是为了解决并发或者共享变量的问题。

博主常年在线,如有错误,欢迎评论指出。
如果喜欢我的文章欢迎关注我的专栏~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值