分析ThreadLocal內(nèi)存泄漏問題
ThreadLocal的實現(xiàn)原理
ThreadLocal
的實現(xiàn):
每一個Thread
內(nèi)部維護一個ThreadLocalMap
映射表,這個映射表的key
是ThreadLocal
實例本身,value
是真正需要存儲的Object
。
也就是說ThreadLocal
本身不存儲值,它只是作為一個key
來讓線程從ThreadLocalMap
獲取value
的。但是ThreadLocalMap
是使用ThreadLocal
的弱引用作為key
的,弱引用的對象在GC
時會被回收。
ThreadLocal為什么會內(nèi)存泄漏
ThreadLocalMap
使用ThreadLocal
的弱引用作為key
,如果一個ThreadLocal
沒有外部強引用來引用它,那么系統(tǒng)GC
的時候,這個ThreadLocal
會被回收,這樣一來,ThreadLocalMap
中會出現(xiàn)key
為null
的Entry
,這樣就沒有辦法訪問key
為null
的Entry
的value
,如果當前線程遲遲不結(jié)束,這些key
為null
的Entry
的value
就會存在一條強引用鏈,永遠無法回收,造成內(nèi)存泄漏。
其實ThreadLocal
的設(shè)計中已經(jīng)考慮到了這種情況,也加上了一些預(yù)防措施,在調(diào)用get
、set
、remove
方法的時候,會清楚線程ThreadLocalMap
里所有key
為null
的value
。
但是這些被動的預(yù)防措施并不能保證不會內(nèi)存泄漏:
- 使用static的ThreadLocal,延長了ThreadLocal的生命周期,可能導致的內(nèi)存泄漏。
- 分配使用了ThreadLocal又不再調(diào)用get() ,set() ,remove() 方法,那么就會導致內(nèi)存泄漏。
為什么使用弱引用
從表面上看內(nèi)存泄漏的根本原因是使用了弱引用,那么為什么使用弱引用而不使用強引用呢?下面看看官方文檔的說法:
To help deal with very large and long-lived usages, the hash table entries use WeakReferences for keys.
翻譯過來就是:為了應(yīng)對非常大和長時間的用途,哈希表使用弱引用。
下面我們分兩種情況討論:
- key 使用強引用:引用的ThreadLocal的對象被回收了,但是ThreadLocalMap還持有ThreadLocal的強引用,如果沒有手動刪除,ThreadLocal不會被回收,導致Entry內(nèi)存泄漏。
- key 使用弱引用:引用的ThreadLocal的對象被回收了,由于ThreadLocalMap持有ThreadLocal的弱引用,即使沒有手動刪除,ThreadLocal也會被回收。value在下一次ThreadLocalMap調(diào)用set,get,remove的時候會被清除。
比較兩種情況,我們可以發(fā)現(xiàn):由于ThreadLocalMap的生命周期跟Thread一樣長,如果都沒有手動刪除對應(yīng)key,都會導致內(nèi)存泄漏,但是使用弱引用可以多一層保障:弱引用 ThreadLocal 不會內(nèi)存泄漏,對應(yīng)的 value 在下一次 ThreadLocalMap 調(diào)用 set , get , remove 的時候會被清除。
因此,ThreadLocal內(nèi)存泄漏的根源是:由于ThreadLocalMap的生命周期跟Thread一樣長,如果沒有手動刪除對應(yīng)key就會導致內(nèi)存泄漏,而不是因為弱引用。
ThreadLocal最佳實踐
綜合上面的分析,我們可以理解ThreadLocal內(nèi)存泄漏的前因后果,那么怎么避免內(nèi)存泄漏呢?
- 每次使用完ThreadLocal,都調(diào)用它的remove() 方法,清除數(shù)據(jù)。
在使用線程池的情況下,沒有及時清理ThreadLocal,不僅是內(nèi)存泄漏的問題,更嚴重的是可能導致業(yè)務(wù)邏輯出現(xiàn)問題。所以,使用ThreadLocal就跟加鎖完要解鎖一樣,用完就清理。
到此這篇關(guān)于分析ThreadLocal內(nèi)存泄漏問題的文章就介紹到這了,更多相關(guān)ThreadLocal內(nèi)存泄漏內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
IntelliJ IDEA Project窗口的一些設(shè)置詳解
這篇文章主要介紹了IntelliJ IDEA Project窗口的一些設(shè)置詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-08-08使用spring boot開發(fā)時java對象和Json對象轉(zhuǎn)換的問題
這篇文章主要介紹了使用spring boot開發(fā)時java對象和Json對象轉(zhuǎn)換的問題,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-03-03手把手教你SpringBoot快速集成Swagger的配置過程
這篇文章主要介紹了手把手教你SpringBoot快速集成Swagger的配置過程,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-02-02java 打印一字符串,并在main()方法內(nèi)調(diào)用它
編寫一個方法(名字自定,但要符合Java編碼規(guī)范),方法內(nèi)打印一字符串,并在main()方法內(nèi)調(diào)用它。2017-02-02解決rror updating database.Cause:java.sql.SQLSyntaxE
這篇文章主要介紹了解決rror updating database.Cause:java.sql.SQLSyntaxErrorException問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-05-05SpringSceurity實現(xiàn)短信驗證碼功能的示例代碼
這篇文章主要介紹了SpringSceurity實現(xiàn)短信驗證碼功能的示例代碼,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-06-06