標籤:
ThreadLocal通過中文解釋就是執行緒區域變數,是線程的一個局部變數。根據哲學家黑格爾“的存在即合理”的說法,ThreadLocal的出現肯定是有它的意義,它的出現也是因為多線程的一個產物。ThreadLocal既然跟線程有關係,那肯定得先對線程瞭解瞭解。
從網上找來了一句話:Java線程的建立,除了堆棧空間,每個線程還需要為線程本機存放區(thread-local storage)和內部資料結構提供一些本機記憶體。
先來學習一下什麼是堆棧空間?
以上是Java虛擬機器運行時資料區的結構圖,看出,堆棧(即圖中的虛擬機器棧)是Java運行時記憶體配置的一部分,這個堆棧的大小可以用過JVM啟動參數Xss設定(JDK5+版本預設好像是1M),那麼,這個堆棧是幹什麼用的呢?大學期間有認真學習過一些技術課程如作業系統、資料結構、C語言等學科的話,不在話下。可惜,我不是這種學生。為了能理解堆棧的一些相關知識,我快速閱覽相關知識繼續學習(現在才知道大學的知識真的是很用,如果再給我4年時間,算了,現在學習也為時不晚)。一句話,它是線程執行方法(位元組碼)的地方,每建立一個線程執行個體,就是分配固定大小(Xss設定參數)的記憶體空間給線程執行方法用,具體結構圖如下:
的每一個棧幀就相當於一個方法的執行,更多可參考《深入理解Java虛擬機器》8.2章節。
上面已經大概的瞭解了一下堆棧,那就再來看看線程的堆棧與本地變數的一個關係結構圖:
我們知道,線程執行個體也是一個對象,對象都是存放在堆裡面的。從可以看出,當建立一個新的線程,那麼就會有一個相對應的堆棧空間建立,那個stack部分就可以很好的解析上文提到的一句話“Java線程的建立,除了堆棧空間,每個線程還需要為執行緒區域儲存(thread-local storage)和內部資料結構提供一些本機記憶體”。這個stack地區就是堆棧空間,而在這個堆棧空間裡面有兩個直線了堆棧對象的引用,一個是線程執行個體的應用,另一個就是本地變數的引用。
總結:
1、 堆棧的溢出不會直接因為ThreadLocal儲存對象的過大的導致堆疊溢位,因為線程堆棧儲存的只是引用。真正的對象還是在堆裡面,如果對象大到超出堆記憶體的限制,反而會導致堆溢出。
2、 從源碼可以看出,每個Thread中都存在一個Map,Map的類型是ThreadLocal.ThreadLocalMap。Map中的key為一個Threadlocal執行個體。這個Map的確使用了弱引用,不過弱引用只是針對key。每個key都弱引用指向Threadlocal。 當把Threadlocal執行個體置為null以後,沒有任何強引用指向Threadlocal執行個體,所以Threadlocal將會被gc回收。但是,我們的value卻不能回收,因為存在一條從current thread串連過來的強引用。只有當前thread結束以後, current thread就不會存在棧中,強引用斷開,Current Thread、 Map,、value將全部被GC回收。
3、 堆棧的生命週期是跟隨著線程的生命週期,當線程池中的線程執行個體還在,堆棧也不會被回收或者清空,所以,原來的ThreadLocalMap還是存在的。所以,當線程池與ThreadLocal同時使用的時候特別需要注意。
【Java】ThreadLocal細節分析