什么是内存泄漏
ThreadLocal
private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table;
int len = parentTable.length;
setThreshold(len);
table = new Entry[len];
for (int j = 0; j < len; j++) {
Entry e = parentTable[j];
if (e != null) {
@SuppressWarnings("unchecked")
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
if (key != null) {
Object value = key.childValue(e.value);
Entry c = new Entry(key, value);
int h = key.threadLocalHashCode & (len - 1);
while (table[h] != null)
h = nextIndex(h, len);
table[h] = c;
size++;
}
}
}
}
每一个ThreadLocal维护一个ThreadLocalMap,key为使用弱引用的ThreadLocal实例,value为线程变量的副本
强引用
弱引用
内存泄漏
如果ThreadLocalMap使用ThreadLocal的强引用
为什么使用弱引用
因为使用弱引用可以多一层保障,弱引用ThreadLocal不会内存泄漏,对应的value在下一次ThreadLocalMap调用set,get,remove时会被清除
因此,ThreadLocal内存泄漏的根本原因是:由于ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除对应的key就会导致内存泄漏,而不是因为弱引用
static class ThreadLocalMap {
//hreadLocalMap中数据是存储在Entry类型数组的table中的,Entry继承了WeakReference(弱引用)
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
**成员变量**
//初始容量
private static final int INITIAL_CAPACITY = 16;
//ThreadLocalMap数据真正存储在table中
private Entry[] table;
//ThreadLocalMap条数
private int size = 0;
//达到这个大小,则扩容
private int threshold;
构造函数
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
//初始化table数组,INITIAL_CAPACITY默认值为16
table = new Entry[INITIAL_CAPACITY];
//key和16取得哈希值
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
//创建节点,设置key-value
table[i] = new Entry(firstKey, firstValue);
size = 1;
//设置扩容阈值
setThreshold(INITIAL_CAPACITY);
}
remove方法
private void remove(ThreadLocal<?> key) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
//如果threadLocalHashCode计算出的下标找到的key和传入key不同,则证明出现哈希冲突,则循环向下查找
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
//如果key相同
if (e.get() == key) {
//删除当前Entry
e.clear();
//清理
expungeStaleEntry(i);
return;
}
}
}
解决方法