天天看點

threadlocal記憶體洩露_ThreadLocal記憶體洩漏問題ThreadLocal記憶體洩漏問題

ThreadLocal記憶體洩漏問題

一、概述

ThreadLocal類用來提供線程内部的局部變量。這些變量在多線程環境下通路(通過get或set方法通路)時能保證各個線程裡的變量相對獨立于其他線程内的變量,ThreadLocal執行個體通常來說都是private static類型。 總結:ThreadLocal不是為了解決多線程通路共享變量,而是為每個線程建立一個單獨的變量副本,提供了保持對象的方法和避免參數傳遞的複雜性。

ThreadLocal的主要應用場景為按線程多執行個體(每個線程對應一個執行個體)的對象的通路,并且這個對象很多地方都要用到。例如:同一個網站登入使用者,每個使用者伺服器會為其開一個線程,每個線程中建立一個ThreadLocal,裡面存使用者基本資訊等,在很多頁面跳轉時,會顯示使用者資訊或者得到使用者的一些資訊等頻繁操作,這樣多線程之間并沒有聯系而且目前線程也可以及時擷取想要的資料。

二、原理

ThreadLocal可以看做是一個容器,容器裡面存放着屬于目前線程的變量。ThreadLocal類提供了四個對外開放的接口方法,這也是使用者操作ThreadLocal類的基本方法:

(1) void set(Object value)設定目前線程的線程局部變量的值。

(2) public Object get()該方法傳回目前線程所對應的線程局部變量。

(3) public void remove()将目前線程局部變量的值删除,目的是為了減少記憶體的占用,該方法是JDK 5.0新增的方法。需要指出的是,當線程結束後,對應該線程的局部變量将自動被垃圾回收,是以顯式調用該方法清除線程的局部變量并不是必須的操作,但它可以加快記憶體回收的速度。

(4) protected Object initialValue()傳回該線程局部變量的初始值,該方法是一個protected的方法,顯然是為了讓子類覆寫而設計的。這個方法是一個延遲調用方法,線上程第1次調用get()或set(Object)時才執行,并且僅執行1次,ThreadLocal中的預設實作直接傳回一個null。 可以通過上述的幾個方法實作ThreadLocal中變量的通路,資料設定,初始化以及删除局部變量,那ThreadLocal内部是如何為每一個線程維護變量副本的呢? 其實在ThreadLocal類中有一個靜态内部類ThreadLocalMap(其類似于Map),用鍵值對的形式存儲每一個線程的變量副本,ThreadLocalMap中元素的key為目前ThreadLocal對象,而value對應線程的變量副本,每個線程可能存在多個ThreadLocal。

三、記憶體洩漏問題

每個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回收。是以得出一個結論就是隻要這個線程對象被gc回收,就不會出現記憶體洩露,但在threadLocal設為null和線程結束這段時間不會被回收的,就發生了我們認為的記憶體洩露。其實這是一個對概念了解的不一緻,也沒什麼好争論的。最要命的是線程對象不被回收的情況,這就發生了真正意義上的記憶體洩露。比如使用線程池的時候,線程結束是不會銷毀的,會再次使用的就可能出現記憶體洩露 。(在web應用中,每次http請求都是一個線程,tomcat容器配置使用線程池時會出現記憶體洩漏問題)

threadlocal記憶體洩露_ThreadLocal記憶體洩漏問題ThreadLocal記憶體洩漏問題

ThreadLocal隻是操作Thread中的ThreadLocalMap,每個Thread都有一個map,ThreadLocalMap是線程内部屬性,ThreadLocalMap生命周期是和Thread一樣的,不依賴于ThreadMap。

ThreadLocal通過Entry儲存在map中,key為Thread的弱引用(GC時會自動回收),value為存入的變量副本,一個線程不管有多少個ThreadLocal,都是通過一個ThreadLocalMap來存放局部變量的,可以再源碼中看到,set值時先擷取map對象,如果不存在則建立,threadLocalMap初始大小為16,當容量超過2/3時會自動擴容。

四、總結

1.使用ThreadLocal,建議用static修飾 static ThreadLocal headerLocal = new ThreadLocal();

2.使用完ThreadLocal後,執行remove操作,避免出現記憶體溢出情況。