【內存泄漏】測試ThreadLocal 在gc後引發的threadLocalMap的key爲null,但value不爲null的情況

效果

gc後key爲null,但是值不爲null。
需要注意的是,這裏立即釋放了對threadLocal實例的強引用,幫助gc回收查看弱引用的使用方法

在這裏插入圖片描述

原因

ThreadLocal#set後會將threadLocal實例本身作爲key 放入 Thread.currentThread().threadLocalMap中,與set的value構成一對Entry。而Entry使用了threadLocal的實例作爲 弱引用。因此當發生gc的時候,弱引用的key會被回收掉,而作爲強引用的value還存在。

作爲key的弱引用的ThreadLocal
在這裏插入圖片描述
此次借用網圖幫助理解
在這裏插入圖片描述

注意

正如我在註釋中寫的那樣,如果沒有失去對ThreadLocal本身的強引用,那麼不會回收threadLocal。

而我們平時代碼中寫的那樣,使用static修飾threadLocal保留一個全局的threadLocal方便傳遞其他value(threadLocal一直被強引用)。這樣就不會讓gc回收 作爲key的threadLocal。即不會導致key爲null。

使用ThreadLocal關鍵之處還是在於,使用完畢要記得remove。特別是在線程池中使用的時候。(否則會等到下一次set的時候才替換掉value–>作爲key的threadLocal爲同一個所以是替換)

threadLocal被強引用 引用,無法被回收
在這裏插入圖片描述

代碼

/**
 * 測試ThreadLocal 在gc後引發的threadLocalMap的key爲null,但value不爲null的情況
 * @Author thewindkee
 * @Date 2019/12/27 9:28
 */
public class Test {

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InterruptedException {
        Thread t = new Thread(()->test("abc",false));
        t.start();
        t.join();
        System.out.println("--gc後--");
        Thread t2 = new Thread(() -> test("def", true));
        t2.start();
        t2.join();
    }

    private static void test(String s,boolean isGC)  {
        try {
            ThreadLocal<Object> threadLocal = new ThreadLocal<>();
            threadLocal.set(s);
            threadLocal = null;//失去對threadLocal的強引用 ,help gc
            if (isGC) {
                System.gc();
            }
            Thread t = Thread.currentThread();
            Class<? extends Thread> clz = t.getClass();
            Field field = clz.getDeclaredField("threadLocals");
            field.setAccessible(true);
            Object threadLocalMap = field.get(t);
            Class<?> tlmClass = threadLocalMap.getClass();
            Field tableField = tlmClass.getDeclaredField("table");
            tableField.setAccessible(true);
            Object[] arr = (Object[]) tableField.get(threadLocalMap);
            for (Object o : arr) {
                if (o != null) {
                    Class<?> entryClass = o.getClass();
                    Field valueField = entryClass.getDeclaredField("value");
                    Field referenceField = entryClass.getSuperclass().getSuperclass().getDeclaredField("referent");
                    valueField.setAccessible(true);
                    referenceField.setAccessible(true);
                    System.out.println(String.format("弱引用key:%s,值:%s", referenceField.get(o), valueField.get(o)));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

強引用、軟引用、弱引用、虛引用測試
ThreadLocal爲什麼會內存泄漏

發佈了118 篇原創文章 · 獲贊 47 · 訪問量 32萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章