关于ThreadLocal内存泄露验证

193 阅读1分钟

废话不多说直接贴代码

public class C {
    static ThreadLocal<String> threadLocal = new ThreadLocal();

    public static void main(String[] args) {
        threadLocal.set("hello world");
        clearWeakReference();
        //下面可以通过反射获取value
        reflectGet();
        //get 在ThreadLocalMap中会对threadLocal重新hash计算到对应下标后会把key重新赋值,value为null value被释放
        get();
    }

    public static void clearWeakReference(){
        try {
            Thread thread = Thread.currentThread();
            Field field = thread.getClass().getDeclaredField("threadLocals");
            field.setAccessible(true);
            Object map = field.get(Thread.currentThread());
            Class c = map.getClass();
            field = c.getDeclaredField("table");
            field.setAccessible(true);
            Object[] obj = (Object[])field.get(map);
            for (int i = 0; i < obj.length; i++) {
                Object b = obj[i];
                if (b != null) {
                    field = b.getClass().getSuperclass().getSuperclass().getDeclaredField("referent");
                    field.setAccessible(true);
                    ThreadLocal sss = (ThreadLocal)field.get(b);
                    if(sss == threadLocal){
                        field.set(b, null);
                        System.out.println("释放weakkey");
                    }
                }
            }
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    public static void get() {
        System.out.println(threadLocal.get());
    }

    public static void reflectGet() {
        try {
            Thread thread = Thread.currentThread();
            Field field = thread.getClass().getDeclaredField("threadLocals");
            field.setAccessible(true);
            Object map = field.get(Thread.currentThread());
            Class c = map.getClass();
            field = c.getDeclaredField("table");
            field.setAccessible(true);
            Object[] obj = (Object[])field.get(map);
            for (int i = 0; i < obj.length; i++) {
                Object b = obj[i];
                if (b != null) {
                    Class superClass = b.getClass().getSuperclass().getSuperclass();
                    field = superClass.getDeclaredField("referent");
                    field.setAccessible(true);
                    Object sss = field.get(b);
                    if(sss == null){
                        field = b.getClass().getDeclaredField("value");
                        field.setAccessible(true);
                        Object val = field.get(b);
                        System.out.println("反射获取threadlocal设置的val:" + val);
                    }
                }
            }
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

我认为的内存泄露是内存不可达的一种说法,多出现于c,c++这种需要开发显示申请释放内存的语言。 这段代码模拟了 WeakReference 被回收的场景(水平有限,不知道咋触发gc,堆调的很小也无法触发gc,只能手动设置成null了),threadlocal.get()已经获取不到值了,但是依然可以通过反射获取到value。证明value可达,所以jvm无法回收。网上很多搜索到的关于threadlocal内存泄露我个人认为讲的不是很准确,value依旧可以使用,只是显示调用get拿不到了。java这种不需要显示申请释放内存的语言我浅薄的认为不存在像c,c++那种内存泄露。