赞
踩
测试用例
Thread thread = Thread.currentThread();
ThreadLocal<Integer> tl = new ThreadLocal<>();
tl.set(1);
System.gc(); // 手动提醒下gc
Thread.sleep(100); // 让gc先执行
System.out.println(111);
tl = null; // 断开ThreadLocal引用
System.gc();
Thread.sleep(100);
System.out.println(111); // 断点打这里
可以看到线程中的threadLocals还持有这个ThreadLocalMap的引用,其中Entry中value还是有值的为1,但是referent为null,表示已经被GC
这里可以看Entry的实现
static class Entry extends WeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } } public abstract class Reference<T> { private T referent; /* Treated specially by GC */ Reference(T referent) { this(referent, null); } Reference(T referent, ReferenceQueue<? super T> queue) { this.referent = referent; this.queue = (queue == null) ? ReferenceQueue.NULL : queue; } }
简单来说:ThreadLocalMap还持有Entry对象的引用,所以Entry对象并不会被释放。而由于key是WeakReference中的referent,对于Entry->referent这个关联引用会在gc的时候断开,所以可以看到上面的referent为null,但是ThreadLocalMap中还存在entry对象。所以这样value是会一直被引用的,可能会导致内存溢出的问题存在。
我们可以使用 remove来避免这种情况
Thread thread = Thread.currentThread();
ThreadLocal<Integer> tl = new ThreadLocal<>();
tl.set(1);
System.gc(); // 手动提醒下gc
Thread.sleep(100); // 让gc先执行
System.out.println(111);
tl.remove(); // 调用remove
tl = null; // 断开ThreadLocal引用
System.gc();
Thread.sleep(100);
System.out.println(111); // 断点打这里
这里table里面只有3个了,说明已经被回收了。
ThreadLocal.remove()方法其实就是获取当前线程的threadLocals(ThreadLocalMap)并调用其remove()方法
ps: 可以对照set方法看看实现,这里解决hash冲突的话不是和HashMap一样用拉链法,而是去找下一个没有设置值得结点
private void remove(ThreadLocal<?> key) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
if (e.get() == key) { // 找到对应的ThreadLocal
e.clear(); // this.referent = null; 断开引用
expungeStaleEntry(i);
return;
}
}
}
继续看 expungeStaleEntry 的实现
private int expungeStaleEntry(int staleSlot) { Entry[] tab = table; int len = tab.length; // 将Entry的value也就是ThrealLocal对应的值置为null // 断开tab是对应槽位和Entry的关联 tab[staleSlot].value = null; tab[staleSlot] = null; size--; // 下面是rehash操作不是本次重点 for (i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) { // ...... } return i; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。