Threadlocal 源码分析与内存泄漏

来源:互联网 发布:ipad网络非常慢怎么办 编辑:程序博客网 时间:2024/05/25 12:20

参考链接

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);    }}
0 0