国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

ThreadLocal引發(fā)的內存泄漏分析

這篇具有很好參考價值的文章主要介紹了ThreadLocal引發(fā)的內存泄漏分析。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

預備知識(引用)

Object o = new Object();

這個o,我們可以稱之為對象引用,而new Object()我們可以稱之為在內存中產生了一個對象實例。

ThreadLocal引發(fā)的內存泄漏分析

當寫下?o=null時,只是表示o不再指向堆中object的對象實例,不代表這個對象實例不存在了。

  • 強引用:?就是指在程序代碼之中普遍存在的,類似“Object obj=new Object()”這類的引用,只要強引用還存在,垃圾收集器永遠不會回收掉被引用的對象實例。

  • 軟引用:?是用來描述一些還有用但并非必需的對象。對于軟引用關聯(lián)著的對象,在系統(tǒng)將要發(fā)生內存溢出異常之前,將會把這些對象實例列進回收范圍之中進行第二次回收。如果這次回收還沒有足夠的內存,才會拋出內存溢出異常。在JDK 1.2之后,提供了SoftReference類來實現(xiàn)軟引用。

  • 弱引用:?也是用來描述非必需對象的,但是它的強度比軟引用更弱一些,被弱引用關聯(lián)的對象實例只能生存到下一次垃圾收集發(fā)生之前。當垃圾收集器工作時,無論當前內存是否足夠,都會回收掉只被弱引用關聯(lián)的對象實例。在JDK 1.2之后,提供了WeakReference類來實現(xiàn)弱引用。

  • 虛引用:?也稱為幽靈引用或者幻影引用,它是最弱的一種引用關系。一個對象實例是否有虛引用的存在,完全不會對其生存時間構成影響,也無法通過虛引用來取得一個對象實例。為一個對象設置虛引用關聯(lián)的唯一目的就是能在這個對象實例被收集器回收時收到一個系統(tǒng)通知。在之后,提供了類來實現(xiàn)虛引用

內存泄漏的現(xiàn)象

/**
 * 類說明:ThreadLocal造成的內存泄漏演示
 */
public class ThreadLocalOOM {
    private static final int TASK_LOOP_SIZE = 500;

    final static ThreadPoolExecutor poolExecutor
            = new ThreadPoolExecutor(5, 5,
            1,
            TimeUnit.MINUTES,
            new LinkedBlockingQueue<>());

    static class LocalVariable {
        private byte[] a = new byte[1024*1024*5];/*5M大小的數(shù)組*/
    }

    final static ThreadLocal<LocalVariable> localVariable
            = new ThreadLocal<>();

    public static void main(String[] args) throws InterruptedException {
        Object o = new Object();
        /*5*5=25*/
        for (int i = 0; i < TASK_LOOP_SIZE; ++i) {
            poolExecutor.execute(new Runnable() {
                public void run() {
                    //localVariable.set(new LocalVariable());
                    new LocalVariable();
                    System.out.println("use local varaible");
                    //localVariable.remove();
                }
            });

            Thread.sleep(100);
        }
        System.out.println("pool execute over");
    }

}

首先只簡單的在每個任務中new出一個數(shù)組

ThreadLocal引發(fā)的內存泄漏分析

?可以看到內存的實際使用控制在25M左右:因為每個任務中會不斷new出一個5M的數(shù)組,5*5=25M,這是很合理的。

ThreadLocal引發(fā)的內存泄漏分析

當我們啟用了ThreadLocal以后

ThreadLocal引發(fā)的內存泄漏分析

?ThreadLocal引發(fā)的內存泄漏分析

內存占用最高升至150M,一般情況下穩(wěn)定在90M左右,那么加入一個ThreadLocal后,內存的占用真的會這么多?

于是,我們加入一行代碼:

ThreadLocal引發(fā)的內存泄漏分析

?再執(zhí)行,看看內存情況:

ThreadLocal引發(fā)的內存泄漏分析

可以看見最高峰的內存占用也在25M左右,完全和我們不加ThreadLocal表現(xiàn)一樣。

這就充分說明,確實發(fā)生了內存泄漏。

分析

根據(jù)我們前面對ThreadLocal的分析,我們可以知道每個Thread 維護一個 ThreadLocalMap,這個映射表的 key 是 ThreadLocal實例本身,value 是真正需要存儲的 Object,也就是說 ThreadLocal 本身并不存儲值,它只是作為一個 key 來讓線程從 ThreadLocalMap 獲取 value。仔細觀察ThreadLocalMap,這個map是使用 ThreadLocal 的弱引用作為 Key 的,弱引用的對象在 GC 時會被回收。

因此使用了ThreadLocal后,引用鏈如圖所示

ThreadLocal引發(fā)的內存泄漏分析

圖中的虛線表示弱引用。

? 這樣,當把threadlocal變量置為null以后,沒有任何強引用指向threadlocal實例,所以threadlocal將會被gc回收。這樣一來,ThreadLocalMap中就會出現(xiàn)key為null的Entry,就沒有辦法訪問這些key為null的Entry的value,如果當前線程再遲遲不結束的話,這些key為null的Entry的value就會一直存在一條強引用鏈:

Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value,而這塊value永遠不會被訪問到了,所以存在著內存泄露。

? 只有當前thread結束以后,current thread就不會存在棧中,強引用斷開,Current Thread、Map value將全部被GC回收。最好的做法是不在需要使用ThreadLocal變量后,都調用它的remove()方法,清除數(shù)據(jù)。

? 其實考察ThreadLocal的實現(xiàn),我們可以看見,無論是get()、set()在某些時候,調用了expungeStaleEntry方法用來清除Entry中Key為null的Value,但是這是不及時的,也不是每次都會執(zhí)行的,所以一些情況下還是會發(fā)生內存泄露。只有remove()方法中顯式調用了expungeStaleEntry方法。

? 從表面上看內存泄漏的根源在于使用了弱引用,但是另一個問題也同樣值得思考:為什么使用弱引用而不是強引用?

下面我們分兩種情況討論:

??key 使用強引用:引用ThreadLocal的對象被回收了,但是ThreadLocalMap還持有ThreadLocal的強引用,如果沒有手動刪除,ThreadLocal的對象實例不會被回收,導致Entry內存泄漏。

??key 使用弱引用:引用的ThreadLocal的對象被回收了,由于ThreadLocalMap持有ThreadLocal的弱引用,即使沒有手動刪除,ThreadLocal的對象實例也會被回收。value在下一次ThreadLocalMap調用set,get,remove都有機會被回收。

? 比較兩種情況,我們可以發(fā)現(xiàn):由于ThreadLocalMap的生命周期跟Thread一樣長,如果都沒有手動刪除對應key,都會導致內存泄漏,但是使用弱引用可以多一層保障。

? 因此,ThreadLocal內存泄漏的根源是:由于ThreadLocalMap的生命周期跟Thread一樣長,如果沒有手動刪除對應key就會導致內存泄漏,而不是因為弱引用。

為什么ThreadLocalMap的key要設置為弱引用?

在 ThreadLocalMap 中的set和get方法中,會對 key為null進行判斷,如果key為null會把value也置為null。
這樣就算忘記調用remove方法,對應的value在下次調用get、set、remove方法中的任意一個都會被清除,從而避免內存泄漏(相當于多了一層保障,但是如果后續(xù)一直不調用這些方法,依然存在內存泄漏的風險,所以最好是及時remove)。

總結

? JVM利用設置ThreadLocalMap的Key為弱引用,來避免內存泄露。

JVM利用調用remove、get、set方法的時候,回收弱引用。

當ThreadLocal存儲很多Key為null的Entry的時候,而不再去調用remove、get、set方法,那么將導致內存泄漏。

使用線程池+?ThreadLocal?時要小心,因為這種情況下,線程是一直在不斷的重復運行的,從而也就造成了value可能造成累積的情況。

錯誤使用ThreadLocal導致線程不安全

/**
 * 非安全的ThreadLocal 演示
 */
public class ThreadLocalUnsafe implements Runnable {

    public static ThreadLocal<Number> numberThreadLocal = new ThreadLocal<Number>();
    /**
     * 使用threadLocal的靜態(tài)變量
     */
    public static Number number = new Number(0);

    public void run() {
        //每個線程計數(shù)加一
        number.setNum(number.getNum() + 1);
        //將其存儲到ThreadLocal中
        numberThreadLocal.set(number);
        //延時2ms
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //輸出num值
        System.out.println("內存地址:"+numberThreadLocal.get() + "," + Thread.currentThread().getName() + "=" + numberThreadLocal.get().getNum());
    }


    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            new Thread(new ThreadLocalUnsafe()).start();
        }
    }

    /**
     * 一個私有的類 Number
     */
    private static class Number {
        public Number(int num) {
            this.num = num;
        }

        private int num;

        public int getNum() {
            return num;
        }

        public void setNum(int num) {
            this.num = num;
        }
    }
}

?輸出:

內存地址:com.test.thread.ThreadLocalUnsafe$Number@5658172e,Thread-2=5
內存地址:com.test.thread.ThreadLocalUnsafe$Number@5658172e,Thread-0=5
內存地址:com.test.thread.ThreadLocalUnsafe$Number@5658172e,Thread-4=5
內存地址:com.test.thread.ThreadLocalUnsafe$Number@5658172e,Thread-1=5
內存地址:com.test.thread.ThreadLocalUnsafe$Number@5658172e,Thread-3=5

? 為什么每個線程都輸出5?難道他們沒有獨自保存自己的Number副本嗎?為什么其他線程還是能夠修改這個值?仔細考察下我們的代碼,我們發(fā)現(xiàn)我們的number對象是靜態(tài)的,所以每個ThreadLoalMap中保存的其實同一個對象的引用,這樣的話,當有其他線程對這個引用指向的對象實例做修改時,其實也同時影響了所有的線程持有的對象引用所指向的同一個對象實例。這也就是為什么上面的程序為什么會輸出一樣的結果:5個線程中保存的是同一Number對象的引用,在線程睡眠的時候,其他線程將num變量進行了修改,而修改的對象Number的實例是同一份,因此它們最終輸出的結果是相同的。

而上面的程序要正常的工作,應該去掉number的static 修飾,讓每個ThreadLoalMap中使用不同的number對象進行操作。

總結:ThreadLocal只保證線程隔離,不保證線程安全。文章來源地址http://www.zghlxwxcb.cn/news/detail-483598.html

到了這里,關于ThreadLocal引發(fā)的內存泄漏分析的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉載,請注明出處: 如若內容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • LeakCanary內存泄漏檢測框架分析。

    LeakCanary內存泄漏檢測框架分析。

    一、什么叫內存泄漏、內存溢出? 內存溢出(out of memory):是指程序在申請內存時,沒有足夠的內存空間供其使用,出現(xiàn)out of memory;比如申請了一個10M的Bitmap,但系統(tǒng)分配給APP的連續(xù)內存不足10M,就會導致內存溢出。 內存泄漏(memory leak):是指程序在申請內存后,無法釋放已申

    2024年02月15日
    瀏覽(22)
  • Valgrind——memcheck內存泄漏分析

    Valgrind——memcheck內存泄漏分析

    valgrind 官網(wǎng) https://www.valgrind.org/ valgrind 是 Linux 業(yè)界主流且非常強大的內存泄漏檢查工具。在其官網(wǎng)介紹中,內存檢查(memcheck)只是其其中一個功能。valgrind 默認使用 memcheck 去檢查內存問題。 valgrind 這個工具不能用于調試正在運行的程序,因為待分析的程序必須在它特定的

    2024年02月02日
    瀏覽(18)
  • jvm內存溢出排查(使用idea自帶的內存泄漏分析工具)

    jvm內存溢出排查(使用idea自帶的內存泄漏分析工具)

    想分析堆內存溢出,一定在運行jar包時就寫上參數(shù) -XX:+HeapDumpOnOutOfMemoryError ,可以看我之前關于如何運行jar包的文章。若你沒有寫??梢詫懮蠀?shù),重啟你的項目,等你的項目發(fā)生下一次堆內存溢出異常,在運行的同級文件夾,將產生類似這樣一個文件 java_pid74935.hprof ,若你

    2024年02月09日
    瀏覽(30)
  • Android 內存泄漏、性能分析常用工具

    一、內存泄漏 1、 MAT-eclipse :“Memory Analyzer Tool”,一個基于Eclipse的內存分析工具,是一個快速、功能豐富的JAVA heap分析工具,它可以幫助我們查找內存泄漏和減少內存消耗。 2、Leakcanary :一款開源的自動檢測內存泄漏的工具。 3、AndroidStudio Profiler :Android Studio 3.0 采用全新

    2024年02月12日
    瀏覽(25)
  • 【Android】一個contentResolver引起的內存泄漏問題分析

    長時間的壓力測試后,系統(tǒng)發(fā)生了重啟,報錯log如下 JNI ERROR (app bug): global reference table overflow (max=51200) global reference table overflow的log 08-08 04:11:53.052912 ??973 ?3243 F zygote64: indirect_reference_table.cc:256] JNI ERROR (app bug): global reference table overflow (max=51200) 08-08 04:11:53.053014 ??973 ?3243 F z

    2024年02月08日
    瀏覽(25)
  • eclipse memory Analyzer(MAT) 內存泄漏分析

    eclipse memory Analyzer(MAT) 內存泄漏分析

    ? 1.1軟件下載 ????????Eclipse IDE,它非常有用。因為Memory Analyzer在分析堆內存的時候比較耗費內存,而Eclipse IDE本身又是比較耗費內存的,所以推薦使用獨立安裝的Memory Analyzer。 安裝包地址:https://www.eclipse.org/mat/downloads.php 獨立安裝的Memory Analyzer的獨立版本所需的最低Ja

    2024年02月14日
    瀏覽(42)
  • C/C++內存泄漏原因分析與應對方法

    C/C++內存泄漏原因分析與應對方法

    一、內存泄漏的危害: 內存泄漏會導致當前應用程序消耗更多的內存,使得其他應用程序可用的內存更少了。 如果有個進程可用的內存不夠,就會觸發(fā)Linux操作系統(tǒng)的直接/后臺內存回收(即將一些內存頁的數(shù)據(jù)寫到磁盤里,那么該頁也就可用了,臟頁回寫)。雖然后臺回收

    2024年02月16日
    瀏覽(24)
  • Linux下使用valgrind分析C++程序的內存泄漏

    Linux下使用valgrind分析C++程序的內存泄漏

    這篇文章介紹一下Linux系統(tǒng)中如何使用valgrind分析C++程序的內存泄漏。 下載地址:官網(wǎng)。 或者直接使用命令 apt install valgrind 安裝。 先看代碼 我們可以看到在 Func() 函數(shù)中存在內存泄漏的問題。 編譯生成一個帶調試信息的可執(zhí)行程序main,命令 g++ -g ./main.cpp -o main 。 然后使用

    2024年01月22日
    瀏覽(31)
  • Android內存泄漏分析及檢測工具LeakCanary簡介,Android進階

    Android內存泄漏分析及檢測工具LeakCanary簡介,Android進階

    @Synchronized override fun expectWeaklyReachable( watchedObject: Any, description: String ) { if (!isEnabled()) { return } removeWeaklyReachableObjects() val key = UUID.randomUUID() .toString() val watchUptimeMillis = clock.uptimeMillis() val reference = KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue) SharkLog.d { \\\"Watching \\\" +

    2024年04月25日
    瀏覽(17)
  • Java中關于內存泄漏分析和解決方案,都在這里了!

    Java中關于內存泄漏分析和解決方案,都在這里了!

    最近正在熟悉Java內存泄漏的相關知識,上網(wǎng)查閱了一些資料,在此做個整理算是對收獲的一些總結,希望能對各位有所幫助,有問題可以文末留言探討、補充。 如下是整篇文章的結構,所需閱讀時間大約20min 內存泄漏 :對象已經(jīng)沒有被應用程序使用,但是垃圾回收器沒辦法

    2024年02月13日
    瀏覽(20)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包