ThreadLocal
是Java提供的线程局部变量,每个线程都可以通过ThreadLocal
来存储各自的局部变量,这个局部变量对其他线程是不可见的。ThreadLocal
为每个线程提供了一个独立的变量副本,各个线程之间互不影响
Java每个线程都有自己的ThreadLocalMap,里边存着自己私有的对象。Map的Entry里,key为ThreadLocal对象,value即为私有对象T
ThreadLocal 内存泄漏的原因
ThreadLocal
可能会导致内存泄漏。这是由于ThreadLocal
的设计和工作方式决定的。
ThreadLocal
为每个线程存储了一个局部变量的副本,这个副本是存放在线程的 ThreadLocalMap
中,ThreadLocal
作为key,局部变量的副本作为value。重点在于,这个key(ThreadLocal
对象)是对线程的 ThreadLocalMap
中的Entry采用了弱引用。
只要ThreadLocal
对象没有被外部强引用所持有,在GC的时候该对象就会被回收,这样ThreadLocalMap
中就会出现key为null的Entry,这种key为null的Entry就可能引发内存泄漏。
为什么会内存泄漏呢?因为虽然ThreadLocal
已经被回收,但是ThreadLocalMap
依然持有Entry的引用,Entry中有value,就导致这个Entry对象没有被回收,这样就导致了内存泄漏。
所以,在使用ThreadLocal
的时候,一定要注意手动去调用ThreadLocal
的 remove()
方法,清除当前线程中的局部变量。
为什么Entry是弱引用?
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
弱引用(Weak Reference):弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存
ThreadLocal
使用弱引用(Weak References)来引用键(Key),这是一种设计上的权衡,旨在解决线程局部变量生命周期管理的问题。
在ThreadLocal
中,每个线程都持有一个ThreadLocalMap
,而这个ThreadLocalMap
使用ThreadLocal
实例作为键来存储线程特定的值。如果使用强引用(Strong References)来引用这些键,那么只要线程是活的,这些键(即ThreadLocal
对象)和对应的值都不会被垃圾回收器回收,即使外部已经没有对这些ThreadLocal
对象的强引用了。这可能会导致内存泄漏,特别是在使用线程池的情况下,因为线程池中的线程通常是长时间存在的。
使用弱引用作为键的引用方式,则使得ThreadLocal
对象可以被垃圾回收器回收,在ThreadLocal
对象不再有其他地方的强引用时,即使线程本身还在运行,ThreadLocalMap
中的相应条目的键会被回收。但这种设计同时引入了一种新的问题,即可能出现键为null的条目(因为键是弱引用,可能被垃圾回收器清除),但这些条目的值却可能长时间占用内存。为解决这个问题,ThreadLocalMap
实现了一种清理机制,该机制会在访问ThreadLocalMap
时清理键为null的条目。不过,完全依赖这种清理机制可能不够及时,因此还建议显式调用ThreadLocal
的remove()
方法来及时清理不再使用的数据,尤其在使用完线程局部变量后。
总的来说,ThreadLocal
使用弱引用主要是为了在ThreadLocal
对象不再被使用时,能够让垃圾回收器有机会回收这些对象,以减轻内存泄漏的风险。