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

為什么ThreadLocal容易導致內(nèi)存泄漏?

開發(fā) 前端
我們通常創(chuàng)建的變量可以被任何線程訪問和修改,而是用ThreadLocal創(chuàng)建的變量只能通過當前線程去訪問和修改。

[[416507]]

本文轉(zhuǎn)載自微信公眾號「三不猴子」,作者sanbuhouzi。轉(zhuǎn)載本文請聯(lián)系三不猴子公眾號。

為什么ThreadLocal容易導致內(nèi)存泄漏?

ThreadLocal是什么?

官方解釋為:

This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable.

我們通常創(chuàng)建的變量可以被任何線程訪問和修改,而是用ThreadLocal創(chuàng)建的變量只能通過當前線程去訪問和修改。

ThreadLocal原理

jdk版本1.8,我們先看一下ThreadLocal的源碼,先從set方法開始。

  1. /** 
  2.    * Sets the current thread's copy of this thread-local variable 
  3.    * to the specified value.  Most subclasses will have no need to 
  4.    * override this method, relying solely on the {@link #initialValue} 
  5.    * method to set the values of thread-locals. 
  6.    * 
  7.    * @param value the value to be stored in the current thread's copy of 
  8.    *        this thread-local
  9.    */ 
  10.   public void set(T value) { 
  11.       Thread t = Thread.currentThread(); 
  12.       ThreadLocalMap map = getMap(t); 
  13.       if (map != null
  14.           map.set(this, value); 
  15.       else 
  16.           createMap(t, value); 
  17.   } 

這個ThreadLocalMap是ThreadLocal的一個內(nèi)部類,key是當前Thread對象,value是我們要存的對象。首先拿到當前線程對象,然后獲取了個map,然后往這個map中放了當前ThreadLocal對象,如果map為空則創(chuàng)建一個map??纯磄etMap的邏輯。

  1. /** 
  2.     * Get the map associated with a ThreadLocal. Overridden in 
  3.     * InheritableThreadLocal. 
  4.     * 
  5.     * @param  t the current thread 
  6.     * @return the map 
  7.     */ 
  8.    ThreadLocalMap getMap(Thread t) { 
  9.        return t.threadLocals; 
  10.    } 

getMap就是在Thread成員變量中獲取一個map。往下就是ThreadLocalMap.set()看看set的邏輯。

  1. /** 
  2.      * Set the value associated with key
  3.      * 
  4.      * @param key the thread local object 
  5.      * @param value the value to be set 
  6.      */ 
  7.     private void set(ThreadLocal<?> key, Object value) { 
  8.  
  9.         // We don't use a fast path as with get() because it is at 
  10.         // least as common to use set() to create new entries as 
  11.         // it is to replace existing ones, in which case, a fast 
  12.         // path would fail more often than not
  13.  
  14.         Entry[] tab = table
  15.         int len = tab.length; 
  16.         int i = key.threadLocalHashCode & (len-1); 
  17.  
  18.         for (Entry e = tab[i]; 
  19.              e != null
  20.              e = tab[i = nextIndex(i, len)]) { 
  21.             ThreadLocal<?> k = e.get(); 
  22.  
  23.             if (k == key) { 
  24.                 e.value = value; 
  25.                 return
  26.             } 
  27.  
  28.             if (k == null) { 
  29.                 replaceStaleEntry(key, value, i); 
  30.                 return
  31.             } 
  32.         } 
  33.  
  34.         tab[i] = new Entry(key, value); 
  35.         int sz = ++size
  36.         if (!cleanSomeSlots(i, sz) && sz >= threshold) 
  37.             rehash(); 
  38.     } 

這里構(gòu)造了個Entry對象,這個Entry可以看成是map的一行數(shù)據(jù),一個key-value對。再看看Entry的源碼。

  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.             } 
  9.         } 

這個Entry對象竟然是繼承了WeakReference對象。上面的流程用圖畫出來就是這樣的。

總結(jié)起來就是:

  • 每個Thread維護著一個ThreadLocalMap的引用
  • ThreadLocalMap是ThreadLocal的內(nèi)部類,用Entry來進行存儲
  • 調(diào)用ThreadLocal的set()方法時,實際上就是往ThreadLocalMap設置值,key是ThreadLocal對象,value是傳遞進來的對象
  • 調(diào)用ThreadLocal的get()方法時,實際上就是往ThreadLocalMap獲取值,key是ThreadLocal對象

ThreadLocal本身并不存儲值,它只是作為一個key來讓線程從ThreadLocalMap獲取value。

什么是弱引用呢? 為什么ThreadLocal要使用弱引用呢?

官方文檔解釋為:

  1. /** 
  2.  * Weak reference objects, which do not prevent their referents from being 
  3.  * made finalizable, finalized, and then reclaimed.  Weak references are most 
  4.  * often used to implement canonicalizing mappings. 
  5.  * 
  6.  * <p> Suppose that the garbage collector determines at a certain point in time 
  7.  * that an object is <a href="package-summary.html#reachability">weakly 
  8.  * reachable</a>.  At that time it will atomically clear all weak references to 
  9.  * that object and all weak references to any other weakly-reachable objects 
  10.  * from which that object is reachable through a chain of strong and soft 
  11.  * references.  At the same time it will declare all of the formerly 
  12.  * weakly-reachable objects to be finalizable.  At the same time or at some 
  13.  * later time it will enqueue those newly-cleared weak references that are 
  14.  * registered with reference queues. 
  15.  * 
  16.  * @author   Mark Reinhold 
  17.  * @since    1.2 
  18.  */ 

就是不會被程序計數(shù)器計數(shù)的引用,所以在垃圾回收器回收的時候不管是否有引用都會被回收。由于垃圾回收器是一個優(yōu)先級很低的線程,因此不一定會很快發(fā)現(xiàn)那些只具有弱引用的對象。

ThreadLocal為什么要使用弱引用?

因為當我們存入的對象被置為null的時候,也就是ThreadLocalMap的value為null,ThreadLocalMap的key是弱引用此時在下一次垃圾回收器回收垃圾的時候就可以回收掉這個key-value也是就一個Entry對象了。

既然弱引用是有助于垃圾回收的,那為什么ThreadLocal還是容易導致內(nèi)存泄漏?

弱引用確實是有助于垃圾回收,但是也是有弊端的,假設我們現(xiàn)在存入了一個對象,此時虛擬機gc,將key弱引用回收,但是value依然是強引用,key被回收了,這個value無法通過通過ThreadLocal對象的get方法獲取,它永遠不會被訪問到了,所以存在內(nèi)存泄漏的風險。

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

  • 在ThreadLocal使用前后都調(diào)用remove清理,同時對異常情況也要在finally中清理。 
  • 盡量不要使用全局的ThreadLocal,靜態(tài)變量的生命周期和類的生命周期是一致的,而類的卸載時機可以說比較苛刻,這會導致靜態(tài)ThreadLocal無法被垃圾回收,容易出現(xiàn)內(nèi)存泄漏。

 

責任編輯:武曉燕 來源: 三不猴子
相關推薦

2025-04-01 05:22:00

JavaThread變量

2024-09-29 08:57:25

2018-10-25 15:24:10

ThreadLocal內(nèi)存泄漏Java

2022-05-09 14:09:23

多線程線程安全

2022-10-18 08:38:16

內(nèi)存泄漏線程

2020-09-10 07:40:28

ThreadLocal內(nèi)存

2021-02-18 16:53:44

內(nèi)存ThreadLocal線程

2024-03-22 13:31:00

線程策略線程池

2011-05-24 16:39:09

Cfree()

2022-07-26 07:14:20

線程隔離Thread

2017-02-27 15:43:51

2023-05-29 07:17:48

內(nèi)存溢出場景

2017-06-02 10:57:29

Android內(nèi)存泄漏Dialog

2019-12-17 10:01:40

開發(fā)技能代碼

2023-10-24 09:30:49

Java內(nèi)存

2024-11-29 08:20:23

Rust內(nèi)存泄漏

2024-03-11 08:22:40

Java內(nèi)存泄漏

2023-12-18 10:45:23

內(nèi)存泄漏計算機服務器

2012-02-22 21:28:58

內(nèi)存泄漏

2020-01-14 10:57:39

內(nèi)存泄漏虛擬機
點贊
收藏

51CTO技術(shù)棧公眾號