八股|ThreadLocal的内存泄露

ThreadLocal是个好用的工具类,但是使用不好是会导致内存泄露的。

内存泄露:之前开辟使用的内存空间,在使用完毕后未释放,结果导致一直占据该内存单元,无法被gc回收,导致该内存单元后续无法被使用,直到程序结束。

为什么ThreadLocal可能会导致内存泄露问题?
回答这个问题首先需要知道这个问题:
1.ThreadLocal存储value的原理
当我们创建一个ThreadLocal对象时,调用set方法,保存value值,当我们需要使用这个value值的时候,只需要调用get方法即可得到value,那么这个value是保存在ThreadLocal对象之中吗?并不是,通过ThreadLocal存储的value是放在一个叫做ThreadLocalMap对象中的,ThreadLocalMap对象是属于线程对象的,一个线程有一个ThreadLocalMap。
当调用ThreadLocal中的set方法保存value值时,value值实际上是保存在了ThreadLocalMap中,那么当调用的时候该如何去ThreadLocalMap中获得value呢?很简单,就是使用ThreadLocal对象作为key,去ThreadLocalMap中查找,能找到其专属value。
在这里插入图片描述

那么回到最初的问题,这种存储模式为什么会导致内存泄露呢?
因为如果ThreadLocal对象被gc回收了,消失了,其对应的value并不会随之被回收,而是依旧保存在ThreadLocalMap之中,由于没有了ThreadLocal对象作为key,对应的value是无法被访问到的。那么此时的value就符合“内存泄漏”的定义了——开辟出来的内存后续无法被回收与使用。


ThreadLocal造成内存泄露的典型场景是使用线程池中的线程:当线程池创建一个线程,执行线程的过程中创建了ThreadLocal对象实例,并保存了value。当线程执行完毕之后并没有被销毁,而是存储保存在了线程池之中,此时ThreadLocal对象与value都依旧存在着,当线程从线程池中拿出再次运行,在执行过程中又创建了新的ThreadLocal对象,那么之前的ThreadLocal对象没有强引用指向,被gc回收,之前保存的value无法被访问到,但依旧有ThreadLocalMap的强引用指向,不会被回收,造成了内存泄露。
其实ThreadLocal的设计者已经考虑到了这点,当set和get等指令执行的时候会自动清除key为null的value,不过万事小心为妙,使用完ThreadLocal之后执行remove操作才保险。


在ThreadLocalMap内部,是有一个叫entry[]的数组保存key-value对的,其实如果从存储与取用的角度去考虑,存储value的时候是不需要存储key的,因为value所在的entry,存储在数组的下标是通过key的哈希函数计算出来的,也就是说,给一个ThreadLocal对象,然后通过一个函数算一下,得到一个下标,这个下标就是这个对象所存储的value。不过这样子的话太不保险了,反正也不费事,不如把key也存储在里面,取用的时候可以做一个保险。举个例子说明一下:
entry[] = [ [张三,300元], [李四,50美金], [王五,100卢布] ]
使用张三去检索,找到一个entry [张三,300元],查看这个entry中的key,指向的是张三,正好是检索用的key,无误,可到张三的value是300元。使用张三,得到一个[李四,50美金],检查一个不对,就重新进行检索。


回归最开始的主题,ThreadLocal为什么可能导致内存泄露?答:由于存储的value并不是存在ThreadLocal对象之中,因此ThreadLocal对象被回收后,value可能无法被回收,这样就造成了内存泄露。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值