ThreadLocal 的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度。但是如果滥用 ThreadLocal ,就可能会导致内存泄漏。下面,我们将围绕三个方面来分析 ThreadLocal 内存泄漏的问题
-
ThreadLocal 实现原理
-
ThreadLocal 为什么会内存泄漏
-
ThreadLocal 最佳实践
ThreadLocal 实现原理
ThreadLocal
ThreadLocal 的实现是这样的:每个 Thread 维护一个 ThreadLocalMap 映射表,这个映射表的 key 是 ThreadLocal 实例本身, value 是真正需要存储的 Object 。
也就是说 ThreadLocal 本身并不存储值,它只是作为一个 key 来让线程从 ThreadLocalMap 获取 value 。值得注意的是图中的虚线,表示 ThreadLocalMap 是使用 ThreadLocal 的弱引用作为 Key 的,弱引用的对象在 GC 时会被回收。
ThreadLocal 为什么会内存泄漏
ThreadLocalMap 使用 ThreadLocal 的弱引用作为 key ,如果一个 ThreadLocal 没有外部强引用来引用它,那么系统 GC 的时候,这个 ThreadLocal 势必会被回收,这样一来, ThreadLocalMap 中就会出现 key 为 null 的 Entry ,就没有办法访问这些 key 为 null 的 Entry 的 value ,如果当前线程再迟迟不结束的话,这些 key 为 null 的 Entry 的 value 就会一直存在一条强引用链: Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value 永远无法回收,造成内存泄漏。
其实, ThreadLocalMap 的设计中已经考虑到这种情况,也加上了一些防护措施:在 ThreadLocal 的 get() , set() , remove() 的时候都会清除线程 ThreadLocalMap 里所有 key 为 null 的 value 。
但是这些被动的预防措施并不能保证不会内存泄漏:
-
使用 static 的 ThreadLocal ,延长了 ThreadLocal 的生命周期,可能导致的内存泄漏。
-
分配使用了 ThreadLoc
