模拟Threadlocal内存泄漏

203 阅读1分钟

ThreadLocal中的key:cache

ThreadLocal<String> cache = ThreadLocal.withInitial(() -> null);

从简写源码中可以看出ThreadLocal中的val引用链

Thead——>ThreadLocal.ThreadLocalMap.Entry——>Object val

所以只要当前线程还存在 val就不会被gc回收

而key只存在一个软引用和我们创建threadLocal对象时的cache引用

当cache不在引用时 并且发生gc key会被回收 而val依然存在 就发生了内存泄漏


class Thead {
    ThreadLocal.ThreadLocalMap threadLocals;
    class ThreadLocal{
        class ThreadLocalMap {
            class Entry extends WeakReference<java.lang.ThreadLocal<?>> {
                Object value;
                Entry(java.lang.ThreadLocal<?> k, Object v) {
                    // k被挂上一个弱引用
                    super(k);
                    // v被挂上value的强引用
                    value = v;
                }
            }
        }
    }
}

下面模拟内存泄漏 创建一个Cache类

class Cache {
    private ThreadLocal<String> threadLocal;

    public void set(String val) {
        if (threadLocal == null) {
            threadLocal = ThreadLocal.withInitial(() -> null);
        }
        threadLocal.set(val);
    }
}

测试类

public class Demo {
    public static void main(String[] args) {
        // 当set方法执行完后cache对象会在gc时被回收 它的属性threadLocal也同样
        set("trf");
        // System.gc();
        Thread thread = Thread.currentThread();
    }

    public static void set(String val) {
        new Cache().set(val);
    }

}

对以上过程进行调试

image.png 未进行gc时 可以发现key和val都还存在

image.png

进行gc后key被回收 而val依然存在 因为它被thead所间接引用 这时在mian方法将无法再删除val 并且只要线程还存在val将一直在内存中

image.png