自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

為何每次用完 ThreadLocal 都要調(diào)用 remove()

開發(fā) 前端
內(nèi)存泄漏指的是,當某一個對象不再有用的時候,占用的內(nèi)存卻不能被回收,這就叫作內(nèi)存泄漏。

[[384900]]

本文轉(zhuǎn)載自微信公眾號「JerryCodes」,作者KyleJerry。轉(zhuǎn)載本文請聯(lián)系JerryCodes公眾號。   
  • 什么是內(nèi)存泄漏
  • Key 的泄漏
  • Value 的泄漏
  • 如何避免內(nèi)存泄露

什么是內(nèi)存泄漏

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

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

下面我們來分析一下,在 ThreadLocal 中這樣的內(nèi)存泄漏是如何發(fā)生的。

Key 的泄漏

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

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

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

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

  1. static class Entry extends WeakReference<ThreadLocal<?>> { 
  2.     /** The value associated with this ThreadLocal. */ 
  3.     Object value; 
  4.  
  5.     Entry(ThreadLocal<?> k, Object v) { 
  6.         super(k); 
  7.         value = v; 
  8.     } 

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

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

Value 的泄漏

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

  1. static class Entry extends WeakReference<ThreadLocal<?>> { 
  2.     /** The value associated with this ThreadLocal. */ 
  3.     Object value; 
  4.  
  5.  
  6.     Entry(ThreadLocal<?> k, Object v) { 
  7.         super(k); 
  8.         value = v; 
  9.     } 

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

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

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

 

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

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

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

JDK 同樣也考慮到了這個問題,在執(zhí)行 ThreadLocal 的 set、remove、rehash 等方法時,它都會掃描 key 為 null 的 Entry,如果發(fā)現(xiàn)某個 Entry 的 key 為 null,則代表它所對應的 value 也沒有作用了,所以它就會把對應的 value 置為 null,這樣,value 對象就可以被正?;厥樟恕?/p>

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

如何避免內(nèi)存泄露

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

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

  1. public void remove() { 
  2.     ThreadLocalMap m = getMap(Thread.currentThread()); 
  3.     if (m != null
  4.         m.remove(this); 

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

所以,在使用完了 ThreadLocal 之后,我們應該手動去調(diào)用它的 remove 方法,目的是防止內(nèi)存泄漏的發(fā)生。

 

責任編輯:武曉燕 來源: JerryCodes
相關推薦

2023-09-22 17:34:37

內(nèi)存remove方法

2024-09-29 08:57:25

2024-07-01 08:01:45

API網(wǎng)關接口

2011-07-29 11:27:02

阿里巴巴谷歌新浪

2015-06-08 14:27:35

谷歌蘋果編程語言

2024-10-31 09:24:42

2024-10-28 08:15:32

2020-07-08 08:07:23

高并發(fā)系統(tǒng)消息隊列

2018-04-09 08:17:36

線程ThreadLocal數(shù)據(jù)

2009-09-29 17:11:23

Hibernate T

2011-07-14 13:50:09

ThreadLocal

2023-10-17 09:41:04

自增主鍵MySQL

2021-01-19 05:24:36

ThreadLocal線程編程

2015-09-09 08:45:49

JavaThreadLocal

2023-10-07 08:26:40

多線程數(shù)據(jù)傳遞數(shù)據(jù)共享

2021-05-06 08:55:24

ThreadLocal多線程多線程并發(fā)安全

2023-08-02 08:54:58

Java弱引用鏈表

2022-05-11 07:36:12

Java線程安全

2021-10-20 22:27:17

Windows 10Windows微軟

2011-07-14 14:15:40

ThreadLocal
點贊
收藏

51CTO技術棧公眾號