ThreadLocal 內存泄漏 代碼演示 實例演示

歡迎關注本人公衆號

在這裏插入圖片描述
閱讀本文前請先閱讀: ThreadLocal內存泄露原因分析

不使用ThreadLocal

下面這段程序創建了一個有5個線程的線程池。
每個線程致性都申請5M大小的堆空間。

public class MyThreadLocalOOM1 {
    public static final Integer SIZE = 500;
    static ThreadPoolExecutor executor = new ThreadPoolExecutor(
            5, 5, 1,
            TimeUnit.MINUTES, new LinkedBlockingDeque<>());

    static class LocalVariable {//總共有5M
        private byte[] locla = new byte[1024 * 1024 * 5];
    }
    public static void main(String[] args) {
        try {
            for (int i = 0; i < SIZE; i++) {
                executor.execute(() -> {
                    new LocalVariable();
                    System.out.println("開始執行");
                });
                Thread.sleep(100);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

使用JDK自帶的VisualVM來觀察對內存佔用情況,下圖中鋸齒狀的藍色區域是堆已經使用的空間大小,可以看到在0-70內,這是因爲每個線程都會申請5M空間,過一小段時間後,就會觸發一次youngGC, 內存就會釋放。
在19:30:36處我手動觸發了一次GC ,可以看到堆空間基本都釋放。
說明LocalVariable全都釋放,未發生內存泄漏。
在這裏插入圖片描述

使用ThreadLocal,但不remove

public class MyThreadLocalOOM2 {
    public static final Integer SIZE = 500;
    static ThreadPoolExecutor executor = new ThreadPoolExecutor(
            5, 5, 1,
            TimeUnit.MINUTES, new LinkedBlockingDeque<>());

    static class LocalVariable {//總共有5M
        private byte[] locla = new byte[1024 * 1024 * 5];
    }

    static ThreadLocal<LocalVariable> local = new ThreadLocal<>();
    public static void main(String[] args) {
        try {
            for (int i = 0; i < SIZE; i++) {
                executor.execute(() -> {
                    local.set(new LocalVariable());
                    System.out.println("開始執行");
                });
                Thread.sleep(100);
            }            
            local = null;//這裏設置爲null,依舊會造成內存泄漏
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

上面代碼中定義了static的ThreadLocal變量local, 但是當for循環致性完畢後,又將local設置爲null。普通對象,此時就沒有強引用了,當GC時就會被回收掉。
但是通過下面圖可以看到,即使for循環結束後手動觸發了GC,堆內存空間依舊佔用約25MB空間,正好是線程池中5個線程的LocalVariable對象的空間和。
所以發生了內存泄漏。
發生內存泄漏的原因見 ThreadLocal內存泄露原因分析
在這裏插入圖片描述

使用Thread Local,且remove

public class MyThreadLocalOOM3 {
    public static final Integer SIZE = 500;
    static ThreadPoolExecutor executor = new ThreadPoolExecutor(
            5, 5, 1,
            TimeUnit.MINUTES, new LinkedBlockingDeque<>());
 
    static class LocalVariable {//總共有5M
        private byte[] locla = new byte[1024 * 1024 * 5];
    }
 
    final static ThreadLocal<LocalVariable> local = new ThreadLocal<>();
    public static void main(String[] args) {
        try {
            for (int i = 0; i < SIZE; i++) {
                executor.execute(() -> {
                    local.set(new LocalVariable());
                    System.out.println("開始執行");
                    local.remove();
                });
                Thread.sleep(100);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

上面代碼中,線程致性完成後,都調用了local.remove()來將threadLocal內的對象刪除。下圖中可以看到在手動觸發GC後,對內存全部釋放,未發生內存泄漏。

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章