您的位置:首页 > 其它

Threadlocal 源码分析与内存泄漏

2016-11-21 22:18 309 查看
参考链接

ThreadLocal源码分析

public T get() {
// 获取当前线程强引用
Thread t = Thread.currentThread();
// 获取ThreadLocalMap,是线程对象thread.threadLocals
// 实际意义上ThreadLocal不存在Map,而是Thread类里存在Map
ThreadLocalMap map = getMap(t);
if (map != null) {
// 获取其Entry
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}

ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}


首先Map不存在于ThreadLocal,而是在Thread中Map

// 继承自弱引用
static class Entry extends WeakReference<ThreadLocal<?>> {

// 这个value引用着Thread对应的副本对象
Object value;

Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}


对象原理:



每个thread中都存在一个map,map的类型是ThreadLocal.ThreadLocalMap,Map中的key为一个threadlocal实例。

这个Map的确使用了弱引用,不过弱引用只是针对key,每个key都弱引用指向threadlocal,当threadlocal tl=null时,GC确实会回收tl实例对象。

但是,我们的value却不能回收,因为存在一条从current thread连接过来的强引用,Thread实例的Map带有value的强引用。只有当前thread结束以后,current thread就不会存在栈中,强引用断开,Current Thread, Map, value将全部被GC回收。

从中可以看出,弱引用只存在于key上,所以key会被回收。而value还存在着强引用,只有thead退出以后,thread中的map被销毁,value的强引用链条才会断掉。

所以得出一个结论就是只要这个线程对象被gc回收,就不会出现内存泄露,但在threadLocal设为null和线程结束这段时间不会被回收的,就发生了我们认为的内存泄露。

Java为了最小化减少内存泄露的可能性和影响,在ThreadLocal的get,set的时候都会清除线程Map里所有key为null的value。

降低内存泄露可能的写法

public class Test2 {

/**
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {

@Override
public void run() {
ThreadLocal tl = new MyThreadLocal();
tl.set(new My50MB());

tl=null;

System.out.println("Full GC");
System.gc();

}

}).start();

System.gc();
Thread.sleep(1000);
System.gc();
Thread.sleep(1000);
System.gc();
Thread.sleep(1000);

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: