最近看多线程相关知识的时候了解到了 ThreadLocal 的内存泄漏问题,但一直不太清晰。今天又找了几个解析看了看,记录下我的理解。
对于每一个 Thread ,都有一个 ThreadLocalMap ,这个 ThreadLocalMap 的键值对是 <ThreadLocal,Object>。所以可以调用 ThreadLocal.set( Object ) ,实际上放进去的是 <this,Object>。可以看下 ThreadLocal 的代码:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
...
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
而这个 ThreadLocalMap 里面实现是这样的:
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
......省略部分代码
private Entry[] table;
......
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
......
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
......
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
......
}
可以看到 Entry 继承了 WeakReference<ThreadLocal<?>>,所以 Entry 创建的时候会用传进来的ThreadLocal 传给它的父类去初始化一个对象(类的对象构造规则是从父类执行到子类) WeakReference<ThreadLocal<?>> ,也就是创建了一个弱引用指向传进来的 ThreadLocal 。
当外部调用 ThreadLocal.set() 的强引用没了,这个弱引用就会在 GC 时被回收。此时 Entry 只剩下 Value 了。当 Thread 长期存活而 ThreadLocal 又不再使用,Entry 的 value 应当被回收,但是因为 Entry 被 ThreadLocalMap 强引用,而 ThreadLocalMap 被 Thread 强引用,所以无法回收这个 Entry ,也就是发生了内存泄露。