[一句话版本]
如果ThreadLocal存了值,且在清除ThreadLocal之前未使用remove()方法清除值,那么这个存的值就找不到了,但是这个值还在被强ThreadLocalMap强引用,就发生了内存泄漏(一般在使用綫程池时问题较大,因为线程不会销毁,内存泄漏一直在)。
前置知识
- 一个对象被强引用时,垃圾回收器不会回收这个对象,即使系统内存不足也不会回收。只有当该对象的强引用被显式地释放,或者不再被任何引用关联时,该对象才会成为垃圾回收的候选对象,且回收条件是内存不够用了。
- 一个对象只被弱引用时,垃圾回收在内存够用时也会收走。四种引用
- 每个线程对象Thread都会强引用ThreaLocalMap(存ThreadLocal的数据用的)。
- ThreaLocalMap的Key弱引用ThreadLocal。
内存泄漏原因
假设:
现在有一个ThreadLocal对象tl2被销毁,那么tl2强引用的堆中的ThreadLocal对象也会在内存不够用时回收,自然地,该对象弱引用的key2(key就是一个ThreadLocal对象)也会被回收。
但是,当线程存在,也就是Thread还没被销毁时,Thread会强引用ThreaLocalMap,此时ThreaLocalMap中的key2被销毁了,但是其对应的value2还没被销毁,但是也招不到了,此时就泄漏了。
代码:
从下面代码可以看到key是被LocalMap弱引用的,而value是被强引用的。那么当外部不强引用key时,那么key就被回收,但value仍在localmap中。
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;
}
}
...
}
为什么key不用强引用?
如果为弱引用,当外部不用threadlocal时,key会自动被清理为null,后续再对threadlocal操作时可以顺带查找key为null的entry把value清理了。但是如果key为强引用,那么key就有值,后续就不知道这个key是否被外部引用,也就不能清理了。所以,总的来说,设为弱引用能够帮助我们更简单 地清理泄漏的缓存。
更详细分析为什么这么设计