91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

為什么每次用完ThreadLocal都要調用remove()

發布時間:2021-10-18 11:52:09 來源:億速云 閱讀:129 作者:iii 欄目:web開發

這篇文章主要介紹“為什么每次用完ThreadLocal都要調用remove()”,在日常操作中,相信很多人在為什么每次用完ThreadLocal都要調用remove()問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”為什么每次用完ThreadLocal都要調用remove()”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

什么是內存泄漏

內存泄漏指的是,當某一個對象不再有用的時候,占用的內存卻不能被回收,這就叫作內存泄漏。

因為通常情況下,如果一個對象不再有用,那么我們的垃圾回收器  GC,就應該把這部分內存給清理掉。這樣的話,就可以讓這部分內存后續重新分配到其他的地方去使用;否則,如果對象沒有用,但一直不能被回收,這樣的垃圾對象如果積累的越來越多,則會導致我們可用的內存越來越少,最后發生內存不夠用的  OOM 錯誤。

下面我們來分析一下,在 ThreadLocal 中這樣的內存泄漏是如何發生的。

Key 的泄漏

在上一講中,我們分析了 ThreadLocal 的內部結構,知道了每一個 Thread 都有一個  ThreadLocal.ThreadLocalMap 這樣的類型變量,該變量的名字叫作 threadLocals。線程在訪問了 ThreadLocal  之后,都會在它的 ThreadLocalMap 里面的 Entry 中去維護該 ThreadLocal 變量與具體實例的映射。

我們可能會在業務代碼中執行了 ThreadLocal instance = null 操作,想清理掉這個 ThreadLocal 實例,但是假設我們在  ThreadLocalMap 的 Entry 中強引用了 ThreadLocal 實例,那么,雖然在業務代碼中把 ThreadLocal 實例置為了  null,但是在 Thread 類中依然有這個引用鏈的存在。

GC 在垃圾回收的時候會進行可達性分析,它會發現這個 ThreadLocal 對象依然是可達的,所以對于這個 ThreadLocal  對象不會進行垃圾回收,這樣的話就造成了內存泄漏的情況。

JDK 開發者考慮到了這一點,所以 ThreadLocalMap 中的 Entry 繼承了 WeakReference 弱引用,代碼如下所示:

static class Entry extends WeakReference<ThreadLocal<?>> {     /** The value associated with this ThreadLocal. */     Object value;      Entry(ThreadLocal<?> k, Object v) {         super(k);         value = v;     } }

可以看到,這個 Entry 是 extends  WeakReference。弱引用的特點是,如果這個對象只被弱引用關聯,而沒有任何強引用關聯,那么這個對象就可以被回收,所以弱引用不會阻止  GC。因此,這個弱引用的機制就避免了 ThreadLocal 的內存泄露問題。

這就是為什么 Entry 的 key 要使用弱引用的原因。

Value 的泄漏

可是,如果我們繼續研究的話會發現,雖然 ThreadLocalMap 的每個 Entry 都是一個對 key 的弱引用,但是這個  Entry 包含了一個對 value 的強引用,還是剛才那段代碼:

static class Entry extends WeakReference<ThreadLocal<?>> {     /** The value associated with this ThreadLocal. */     Object value;       Entry(ThreadLocal<?> k, Object v) {         super(k);         value = v;     } }

可以看到,value = v 這行代碼就代表了強引用的發生。

正常情況下,當線程終止,key 所對應的 value  是可以被正常垃圾回收的,因為沒有任何強引用存在了。但是有時線程的生命周期是很長的,如果線程遲遲不會終止,那么可能 ThreadLocal 以及它所對應的  value 早就不再有用了。在這種情況下,我們應該保證它們都能夠被正常的回收。

為了更好地分析這個問題,我們用下面這張圖來看一下具體的引用鏈路(實線代表強引用,虛線代表弱引用):

為什么每次用完ThreadLocal都要調用remove()

可以看到,左側是引用棧,棧里面有一個 ThreadLocal 的引用和一個線程的引用,右側是我們的堆,在堆中是對象的實例。

我們重點看一下下面這條鏈路:Thread Ref &rarr; Current Thread &rarr; ThreadLocalMap &rarr; Entry &rarr; Value &rarr;  可能泄漏的value實例。

這條鏈路是隨著線程的存在而一直存在的,如果線程執行耗時任務而不停止,那么當垃圾回收進行可達性分析的時候,這個 Value  就是可達的,所以不會被回收。但是與此同時可能我們已經完成了業務邏輯處理,不再需要這個 Value 了,此時也就發生了內存泄漏問題。

JDK 同樣也考慮到了這個問題,在執行 ThreadLocal 的 set、remove、rehash 等方法時,它都會掃描 key 為 null 的  Entry,如果發現某個 Entry 的 key 為 null,則代表它所對應的 value 也沒有作用了,所以它就會把對應的 value 置為  null,這樣,value 對象就可以被正常回收了。

但是假設 ThreadLocal 已經不被使用了,那么實際上 set、remove、rehash  方法也不會被調用,與此同時,如果這個線程又一直存活、不終止的話,那么剛才的那個調用鏈就一直存在,也就導致了 value 的內存泄漏。

如何避免內存泄露

分析完這個問題之后,該如何解決呢?解決方法就是我們本課時的標題:調用 ThreadLocal 的 remove  方法。調用這個方法就可以刪除對應的 value 對象,可以避免內存泄漏。

我們來看一下 remove 方法的源碼:

public void remove() {     ThreadLocalMap m = getMap(Thread.currentThread());     if (m != null)         m.remove(this); }

可以看出,它是先獲取到 ThreadLocalMap 這個引用的,并且調用了它的 remove 方法。這里的 remove 方法可以把 key 所對應的  value 給清理掉,這樣一來,value 就可以被 GC 回收了。

所以,在使用完了 ThreadLocal 之后,我們應該手動去調用它的 remove 方法,目的是防止內存泄漏的發生。

到此,關于“為什么每次用完ThreadLocal都要調用remove()”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

宁明县| 开鲁县| 黔西| 阜康市| 北宁市| 中西区| 农安县| 丹巴县| 临澧县| 宣威市| 盐城市| 九江县| 云南省| 榆树市| 阿拉善右旗| 普宁市| 灵山县| 文山县| 民丰县| 永福县| 江都市| 紫金县| 阜新市| 平乡县| 金昌市| 灌南县| 洪洞县| 古蔺县| 大庆市| 龙陵县| 潞西市| 乐山市| 高唐县| 关岭| 永年县| 土默特右旗| 驻马店市| 林州市| 连平县| 四子王旗| 江孜县|