日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢(xún)
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
為什么ThreadLocal容易導(dǎo)致內(nèi)存泄漏?

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

成都創(chuàng)新互聯(lián)公司專(zhuān)注為客戶(hù)提供全方位的互聯(lián)網(wǎng)綜合服務(wù),包含不限于成都網(wǎng)站設(shè)計(jì)、成都做網(wǎng)站、外貿(mào)網(wǎng)站建設(shè)、禹會(huì)網(wǎng)絡(luò)推廣、微信小程序、禹會(huì)網(wǎng)絡(luò)營(yíng)銷(xiāo)、禹會(huì)企業(yè)策劃、禹會(huì)品牌公關(guān)、搜索引擎seo、人物專(zhuān)訪、企業(yè)宣傳片、企業(yè)代運(yùn)營(yíng)等,從售前售中售后,我們都將竭誠(chéng)為您服務(wù),您的肯定,是我們最大的嘉獎(jiǎng);成都創(chuàng)新互聯(lián)公司為所有大學(xué)生創(chuàng)業(yè)者提供禹會(huì)建站搭建服務(wù),24小時(shí)服務(wù)熱線:13518219792,官方網(wǎng)址:www.cdcxhl.com

為什么ThreadLocal容易導(dǎo)致內(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)建的變量可以被任何線程訪問(wèn)和修改,而是用ThreadLocal創(chuàng)建的變量只能通過(guò)當(dāng)前線程去訪問(wèn)和修改。

ThreadLocal原理

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

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

這個(gè)ThreadLocalMap是ThreadLocal的一個(gè)內(nèi)部類(lèi),key是當(dāng)前Thread對(duì)象,value是我們要存的對(duì)象。首先拿到當(dāng)前線程對(duì)象,然后獲取了個(gè)map,然后往這個(gè)map中放了當(dāng)前ThreadLocal對(duì)象,如果map為空則創(chuàng)建一個(gè)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成員變量中獲取一個(gè)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)造了個(gè)Entry對(duì)象,這個(gè)Entry可以看成是map的一行數(shù)據(jù),一個(gè)key-value對(duì)。再看看Entry的源碼。

 
 
 
 
  1. static class Entry extends WeakReference> { 
  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.         } 

這個(gè)Entry對(duì)象竟然是繼承了WeakReference對(duì)象。上面的流程用圖畫(huà)出來(lái)就是這樣的。

總結(jié)起來(lái)就是:

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

ThreadLocal本身并不存儲(chǔ)值,它只是作為一個(gè)key來(lái)讓線程從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.  * 

     Suppose that the garbage collector determines at a certain point in time 

  7.  * that an object is weakly 
  8.  * reachable.  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.  */ 

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

ThreadLocal為什么要使用弱引用?

因?yàn)楫?dāng)我們存入的對(duì)象被置為null的時(shí)候,也就是ThreadLocalMap的value為null,ThreadLocalMap的key是弱引用此時(shí)在下一次垃圾回收器回收垃圾的時(shí)候就可以回收掉這個(gè)key-value也是就一個(gè)Entry對(duì)象了。

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

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

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

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

當(dāng)前題目:為什么ThreadLocal容易導(dǎo)致內(nèi)存泄漏?
標(biāo)題網(wǎng)址:http://www.dlmjj.cn/article/djeoecg.html