高級JAVA開發 Java基礎部分

高級JAVA開發 Java基礎部分

ThreadLocal 以及內存泄漏問題

分析如有不正確之處,請回帖或聯繫我:[email protected]

隨便找一個應用ThreadLocal的例子:
在這裏插入圖片描述
在使用時,聲明一個本地ThreadLocal變量並重寫initialValue()方法,就可以爲每個線程提供一個本地變量存儲的功能。那麼,它是怎麼實現的呢?
在使用時調用ThreadLocal.get()方法,看看get方法的實現:
在這裏插入圖片描述
get方法用當前線程作爲key獲得了一個map,那麼看看這個map是哪兒來的,跟進getMap()方法:
在這裏插入圖片描述在這裏插入圖片描述
由圖可知,map是從Thread類取得的,這個map是Thread類的一個成員變量!
那麼重新整理下思路:Thread類包含了一個map類型的成員變量,默認是null,ThreadLocal相當於一個工具類,提供對這個map的訪問方法,那麼這個map是什麼時候被初始化的呢?當我們用ThreadLocal.get()方法時,拿到的不是null,那返回get()方法查看原因:
在這裏插入圖片描述
在這裏插入圖片描述
這裏看出,當map不是null時用當前ThreadLocal對象作爲key去取value,map是null時,初始化map後,調用我們重寫的initialValue(),把initialValue放到map,然後返回initialValue。
那createMap都幹了點啥???繼續跟:
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
到這裏總結一下:第一次使用get時候初始化了map,也就是Thread類中的map被初始化,並且以當前ThreadLocal實例的弱引用作爲key,initialValue返回值作爲value放到map中。第二次get則用ThreadLocal對象作爲key取得value返回。實例引用關聯關係爲:Thread強引用Map,map中存儲弱引用ThreadLocal(key)和強引用Value
弱引用的生命週期:被弱引用關聯的對象,在垃圾回收時,如果這個對象只被弱引用關聯(沒有任何強引用關聯他),那麼這個對象就會被回收。
Thread不死,Map也不死,ThreadLocal對象在聲明時一直被強引用着,GC就不會回收ThreadLocal,Map中的Entry擁有ThreadLocal的弱引用和Value的強引用,如果持有ThreadLocal的人釋放了強引用,它唯一的弱引用會被GC掉。
恍然大悟!只有當前Thread重新new一個ThreadLocal對象覆蓋原聲明,舊的ThreadLocal再也沒人持有它的強引用會被GC掉,此時的Value還被Entry持有着強引用沒法被GC,就發生了內存泄漏!
而在我們程序中大多將ThreadLocal聲明爲static final的,因爲有static,它的生命週期變爲和當前Class的生命一樣,只有當前Class被卸載的時候,可達分析時static對象不可達會被回收,最大生命化當前ThreadLocal對象。被聲明爲final的變量不可更改。如此聲明ThreadLocal的原因大致是不想讓它輕易消亡,而又能防止內存泄漏吧~

失效的Value何時被回收?
模擬一下,ThreadLocal被替換掉後,在我們自己的程序中必然會重新重寫initialValue方法,調用get()方法後是如何工作的:
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
cleanSomeSlots方法註釋寫到:
在這裏插入圖片描述
翻譯:試探性地掃描一些cell,尋找舊的entry。在添加新元素或刪除另一箇舊元素時調用此函數。它執行對數次掃描,在不掃描(快速但保留垃圾)和掃描次數與元素數量成比例之間保持平衡,這將找到所有垃圾,但會導致一些插入花費O(n)時間。
在這裏插入圖片描述
到這裏已經瞭解到在get和set操作的同時會刪除無效的Entry。

最終總結:如果ThreadLocal被新new了一個,Thread成員變量map中的弱引用key會被回收,value所在的Entry會在下一次調用新ThreadLocal的get或者set方法時被擦除,Entry對象由於沒有其他強引用等着被GC掉。如果在覆蓋舊ThreadLocak對象前想加速GC,ThreadLocal提供了remove方法,remove方法會刪除key的引用後再調用expungeStaleEntry(這裏就不再貼圖了),最終釋放Entry的引用,幫助GC。

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