浅析ThrealLocal内存泄漏

ThreadLocal是提供线程局部变量的类。这些变量与正常变量不同,因为每个访问一个线程(通过其{@code get}或{@code set}方法)的线程都有其自己的,独立初始化的变量副本。

以上取自jdk中ThreadLocal官方注释。

首先,可以打开Thread类,看到ThreadLocal.ThreadLocalMap threadLocals = null; 我们可以看到在Thread类中维护了ThreadLocalMap变量;

然后来看下ThreadLocal的部分方法:

	public ThreadLocal() {
    }
	public void set(T value) {
        // 获取当前线程
        Thread t = Thread.currentThread();
        // 获取当前线程的ThreadLocalMap
        ThreadLocalMap map = getMap(t);
        // 如果当前线程的ThreadLocalMap未实例化,则创建
        if (map != null)
            // 否则set当前Value
            map.set(this, value);
        else
            createMap(t, value);
    }

	// 获取当前线程的ThreadLocalMap
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

	// threadLocals就是Thread的变量
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

我们知道ThreadLocal实际是和具体的线程绑定,上面代码是ThreadLocal的set方法,可以看见,获取了当前的线程信息,然后以当前ThreadLocal为key,将value放到了ThreadLocalMap中;

下面我们来看下ThreadLocalMap的结构

    static class ThreadLocalMap {
        // map是元素为Entry的数组数据,Entry的构造函数有两个元素,一个ThreadLocal,一个是value。这里ThrreadLocal是一个弱引用
        static class Entry extends WeakReference<ThreadLocal<?>> {
            Object value;
            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

        private Entry[] table;

        ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }
    }
补充一下引用的基本概念:
  • 强引用

    正常对象都属于强引用,比如new Object();

  • 软引用 SoftReference

    内存足够的时候不回收,内存不足的时候才会回收

  • 弱引用 WeakReference

    只要进行GC,就会回收

  • 虚引用 PhantomReference

    必须和引用队列使用ReferenceQueue

    引用队列ReferenceQueue:

    ​ 可以和所有的引用一起使用,作用是在GC时会放到引用队列里;

    ​ 相当于这个对象被回收的时候给予通知

所以在ThreadLocalMap中的数据,一旦发生GC,那么key是会被回收的;如果key被回收,就会存在很多<null, value>的键值对;Value因为一直也被引用,所以永远不会回收,除非当前线程结束;

因为ThreadLocalMap在Thread里,他们的生命周期是一致的;

如何正确使用ThreadLocal?
  • 每次使用完ThreadLocal都调用它的remove()方法清除数据
  • 将ThreadLocal变量定义成private static,这样就一直存在ThreadLocal的强引用,也就能保证任何时候都能通过ThreadLocal的弱引用访问到Entry的value值,进而清除掉 。
posted @ 2021-01-29 18:20  faylinn  阅读(121)  评论(0编辑  收藏  举报
、、、