ThreadLocal 的内存泄漏问题

ThreadLocal 存储结构

在这里插入图片描述

ThreadLocal 垃圾回收

从 ThreadLocal 类提供的方法上看,Value 对象是保存在 ThreadLocal 对象内的,但其实 ThreadLocal 类是一个代理类,ThreadLocal 对象内部实际不保存数据,真正用于存储数据的地方是每个 Thread 对象内部的 threadLocals 属性,ThreadLocal 封装了对 Thread 对象内部的 threadLocals 属性的操作。
由于 ThreadLocal 跟线程有关,因此 ThreadLocal 对象的垃圾回收需要从 Thread 对象开始分析。

  1. JVM 扫描变量可达性的时候,部分可达性分析会以 Thread 类的对象为根开始扫描,一个未终止的 Thread 对象不会被回收;
  2. Thread 对象內部的 threadLocals 属性是一个 ThreadLocalMap 对象,与 Thread 对象是强引用关系,如果没有主动将 threadLocals 引用指向 null,那么只要 Thread 对象没有被回收 threadLocals 指向的对象也不会被回收;
  3. ThreadLocalMap 对象内部主要是 Entry 数组,Entry 结构是一个类似 Pair<Key,Value> 的结构,而 Entry 类继承了 WeakReference 类,也就是说 Key 部分相当于是一个 WeakReference<ThreadLocal> 结构的弱引用,Value 部分就是 ThreadLocal 指定的泛型类型的值。
  4. Entry 结构的 Key 部分是由 WeakReference 类包装的,而 WeakReference 类在发生 GC 的时候,WeakReference 类对象内的 referent 属性的指向会被消除指向 null,也就是说 WeakReference (Entry) 对象与 ThreadLocal 对象之间的引用关系被消除了,但是 Entry 对象本身的引用关系没有变化,而 ThreadLocalMap 对象里的 Entry 数组还存有 Entry 对象的强引用,因此在发生 GC 的时候 Entry 对象不会被回收。
  5. ThreadLocal 对象本身被回收:在 GC 前就主动调用了 ThreadLocal 的 remove 方法,没有调用也没关系,因为当发生 GC 时,Entry 对象与 ThreadLocal 对象之间的引用关系就会被强制消除,也就是说 Entry 对象的 Key 引用被消除了,此时 ThreadLocal 对象是否被回收,分为两种情况:
    • 如果 ThreadLocal 对象是在业务逻辑执行中 new 出来的临时变量,那么业务逻辑执行完了之后,ThreadLocal 对象与 GC ROOT 就没有强引用关系了,就会被回收;
    • 如果有其它不会被回收的对象引用了该 ThreadLocal 对象,也就是说 ThreadLocal 对象与 GC ROOT 还有强引用关系,那么不会被回收,例如:声明的 ThreadLocal 对象是 static 的、或者虽然 new 出来的是临时变量但是业务逻辑还没有执行完;
  6. ThreadLocal 对象管理的 Value 对象被回收:Value 对象被回收强依赖于 ThreadLocal 的 set、get 和 remove 方法,这3个方法內部附带了 ThreadLocal 垃圾回收的动作,会依据算法部分扫描 Entry 数组(Thread -> ThreadLocalMap -> Entry<Key, Value>[]),如果发现 Entry 对象的 Key 引用已经被消除了,也就是说 Key 指向了 null,就会把 Value 置为 null,Entry 置为 null,此时 Value 对象能否在下一次 GC 的时候被回收,分为两种情况:
    • 如果 Value 对象是在业务逻辑执行中 new 出来的临时变量,那么业务逻辑执行完了之后,Value 对象与 GC ROOT 就没有强引用关系了,Value 对象在下一次 GC 的时候就能被 JVM 回收掉了;
    • 如果有其它不会被回收的对象引用了该 Value 对象,也就是说在下一次 GC 的时候 Value 对象与 GC ROOT 还有强引用关系,那么不会被回收,例如:虽然 new 出来的是临时变量但是业务逻辑还没有执行完;

ThreadLocal 内存泄漏

  1. ThreadLocal 对象本身的内存泄漏问题
    • 因为在实际保存的时候使用了 WeakReference 包装,因此不会有内存泄漏的问题
  2. ThreadLocal 对象管理的 Value 对象的内存泄漏问题
    • 如果没有及时调用 ThreadLocal 对象的 remove 方法,在发生 GC 后 ThreadLocalMap 中的 KEY 就为 null 了,此时程序就没有办法去访问这个数据了,但是这个数据被 Thread -> ThreadLocalMap -> Entry-> Value 强引用着,JVM 没有办法回收掉,因此内存泄漏了。
    • 但是如果同一个线程又执行了任意 ThreadLocal 的 set、get 和 remove 方法,是有可能将 Value 对象的强引用关系消除的,此时 Value 对象就能被回收掉了。

解决 ThreadLocal 内存泄漏

  • 在业务逻辑处理完了,不需要 ThreadLocal 再保存 Value 对象的时候,及时调用 remove 方法,主动清除 Thread 对象到 Value 对象的强引用关系。

参考:
Dubbo中的InternalThreadLocal的簡單分析

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值