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 的任何一个方法的时候会被清除,从而避免内存泄漏。
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/chengqiuming/article/details/122144289
内容来源于网络,如有侵权,请联系作者删除!