ThreadLocalMap 中的 key 使用弱引用会出现内存泄漏吗

x33g5p2x  于2021-12-26 转载在 其他  
字(1.3k)|赞(0)|评价(0)|浏览(220)

一 图解

回收前

回收后

二 图解说明

1 假设在业务代码中使用完 ThreadLocal,threadLocal Ref 被回收。

2 由于 ThreadLocalMap 只持有 ThreadLocal 的弱引用,没有任何强引用指向 threadLocal 实例,所以 threadLocal 就可以顺利被垃圾回收器回收,此时 entry 中的 key = null。

3 但是在没有手动删除 entry 以及 currentThread 依然运行的前提下,也存在强引用链 threadRdf > currentThread > threadLocalMap > entry > value,value 不会被回收,而这块的 value 永远不会被访问到, 导致 value 内存泄漏。

也就是说, ThreadLocalMap 中的 key 使用了弱引用,也有可能内存泄漏。

三 出现内存泄漏的真实原因

内存泄漏发生跟 ThreadLocalMap 中的 key 是否使用弱引用是没有关系的。

那内存泄漏的真正原因是什么呢?

从刚才的分析来看,发生内存泄漏有两个前提:

1 没有手动删除这个 entry

2 CurrentThread 依然运行

第一点好理解,只要在使用然 ThreadLocal,调用其 remove 方法删除对应的 Entry,就能避免内存泄漏。

第二点稍微复杂一点,由于 ThreadLocalMap 是 Thread 的一个属性,被当前线程所使用,所以生命周期跟 Thread 一样长,那么在使用完 ThreadLocal,如果当前 Thread 也随之执行结束,ThreadLocalMap 自然也会被垃圾回收器回收掉,从根源上避免了内存泄漏。

综上,ThreadLocal 内存泄漏的根源是:由于 ThreadLocalMap 的生命周期跟 Thread 一样长,如果没有手动删除对应 key,就会导致内存泄漏。

四 为什么使用弱引用

根据刚才的分析:无论 ThreadLocalMap 中的 key 使用哪种类型引用都无法完全避免内存泄漏,跟使用弱引用没有关系。

要避免内存泄漏有两种方式:

1 使用完 ThreadLocal,调用其 remove 方法删除对应的 Entry。

2 使用完 ThreadLocal,当前 Thread 也随之运行结束。

相对第一种方式,第二种方式显然更不好控制,特别是使用线程池的时候,线程结束时不会被销毁的。

也就是说,只要记得在使用完 ThreadLocal,及时的调用 remove,无论 key 是强引用还是弱引用都不会有问题。那么为什么 key 要用弱引用呢?

事实上,在 ThreadLocalMap 中的 set/getEntry 方法中,会对 key 为 null(也就是 ThreadLocal 为 null )进行判断,如果为 null 的话,那么会对 value 置为 null 的。随后对应的 value 对象就会被垃圾回收。

这就意味着使用完 ThreadLocal,CurrentThread 依然运行的情况下,就算忘记调用 remove 方法,弱引用比强引用可以多一层保障:弱引用使用的 ThreadLocal 会被回收,对应的 value 在下一次 ThreadLocalMap 调用 set/getEntry/remove 的任何一个方法的时候会被清除,从而避免内存泄漏。

相关文章