1.什么情况下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);
}
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
从源码中可以看出,对象的引用为 Thread -> ThreadLocalMap ->ThreadLocalMap内部Entry ->ThreadLocal。只要线程一直存在ThreadLocalMap内部Entry一直存在,由于key对应的ThreadLocal为弱引用,只要不存在强引用,就会GC,value为强引用,如果不在最后调用ThreadLocal.remove(),会一直没办法GC,导致内存泄露。
2.如何防止内存泄漏
根据1中分析,每次使用ThreadLocal.set(),最后调用ThreadLocal.remove()就可以了。
3.为什么Entry中的key即ThreadLocal为弱引用,而value为强引用
ThreadLocal设置为弱引用,是为了防止使用ThreadLocal时重新创建
ThreadLocal<String> threadLocal = new ThreadLocal<>();
threadLocal.set("test1");
设置成弱引用,当前面的代码执行完成时,threadLocal对象失去强引用,只剩下Entry中的弱引用,自然可以被GC。不用担心threadlocal.get()会出现什么问题,因为只要可以调用get()就说明强引用还存在(可达的)
当然,如果一开始就使用的ThreadLocal为静态变量,也不会有这种问题。
value设置为强引用,没办法设置为弱引用,因为我们设置值时一般是这样的
threadLocal.set("test1");
这时候的test1除了Entry中的value引用外,不存在其他强引用,设置为弱引用时会被GC回收,这时候恰好调用threadLocal.get(),这是后就会出现bug。
所以,key和value有这种强弱引用之分,和除了Entry以外有没有强引用有关系。