ThreadLocal儲存結構及記憶體溢位問題分析

2021-10-07 06:51:22 字數 4205 閱讀 1821

threadlocal是為了解決多執行緒併發訪問共享變數時造成資料異常的問題,與加鎖的思想方式不同,threadlocal是通過為每個執行緒提供乙個變數的副本,以此保證併發訪問的安全。

先看一下在沒有使用threadlocal的情況下對於共享變數的訪問結果:

/**

* 啟動兩個執行緒,各執行100次對共享變數count加1,得到的結果可能並不是200,而是乙個無法確定的數

*/public

class

nousethreadlocal

catch

(interruptedexception e)

count++;}

system.out.

println

("count: "

+ count);}

).start()

;new

thread((

)->

catch

(interruptedexception e)

count++;}

system.out.

println

("count: "

+ count);}

).start();}}

我們都知道這樣的結果是無法確定的。

count:

199count:

199

使用threadlocal的方式。

public

class

usethreadlocal

catch

(interruptedexception e)

threadlocal.

set(threadlocal.

get()+

1);}

system.out.

println

("count: "

+ threadlocal.

get())

;}).

start()

;new

thread((

)->

catch

(interruptedexception e)

threadlocal.

set(threadlocal.

get()+

1);}

system.out.

println

("count: "

+ threadlocal.

get())

;}).

start();}}

無論執行多少次得到的結果都是兩個100。

count:

100count:

100

通過這個例子就說明了threadlocal可以保證多個執行緒對共享變數的訪問安全,並且由結果也可以看出,每個執行緒是自己玩自己的,也就是執行緒內部使用變數副本的方式,所以得到的結果是100而不是200。

public

void

set(t value)

//threadlocal中的乙個靜態內部類,執行緒物件持有這樣乙個引用

threadlocalmap getmap

(thread t)

void

createmap

(thread t, t firstvalue)

threadlocalmap

(threadlocal<

?> firstkey, object firstvalue)

public t get()

}return

setinitialvalue()

;}private entry getentry

(threadlocal<

?> key)

static

class

entry

extends

weakreference

?>>

}

thread物件中有乙個threadlocalmap的屬性

threadlocal.threadlocalmap threadlocals = null;
threadlocal儲存結構如下圖:

執行緒物件中有乙個threadlocalmap屬性,threadlocalmap中又有乙個entry陣列物件,當呼叫get或者set方法時都是先找到當前執行緒的threadlocalmap屬性,然後再通過threadlocalmap獲取entry陣列物件,資料就是由entry負責儲存的,entry中的key實際上是通過弱引用的方式引用了threadlocal的例項。

根據我們前面對threadlocal的分析,我們可以知道每個thread 維護乙個 threadlocalmap,threadlocalmap內部又維護乙個entry,entry的 key 是 threadlocal例項本身,value 是真正需要儲存的資料,而key與threadlocal之間又是一種弱引用關係。

先看一張執行緒物件和threadlocal物件的引用關係圖。

當把threadlocal變數置為null以後,沒有任何強引用指向threadlocal例項,只有一條與key關聯的弱引用路徑,所以threadlocal將在下一次gc時被**。這樣一來,threadlocalmap中就會出現key為null的entry,而這些key又是無法被訪問到的,所以如果當前執行緒一直存活的話,就會存在這樣一條強引用鏈:currentthreadref -> currentthread -> threalocalmap -> entry -> value,而這塊value永遠不會被訪問到了,資料也一直存在於記憶體中,記憶體洩露的問題就產生了。

然而大多數我們使用執行緒的場景都是通過執行緒池來管理,而執行緒池剛好是不會真正銷毀執行緒的。

為什麼要使用弱引用?

首先明確一點,記憶體洩露的問題和弱引用沒有任何關係,使用弱引用的原因正是為了方便物件的**和避免記憶體洩露,假設threadlocal的例項與key之間是強引用的關係,那麼即使threadlocal自身的引用斷了以後也不能對threadlocal進行**,因為threadlocal還和key存在著強引用的關係。

所以如果不使用弱引用,那麼當我們threadlocal使用完之後,必須手動清除threadlocal與key之間的引用關係,否則造成的是整個entry物件級別的記憶體洩露。

如何避免記憶體洩露?

現在我們已經知道造成記憶體洩露的根本原因是因為當前執行緒引用著entry物件,導致entry物件不能被**,從而導致value中的資料占用著記憶體空間。

其實要解決此問題,只需要我們使用完之後再手動呼叫remove方法即可。

public

void

remove()

threadlocal記憶體溢位**演示

public

class

threadlocaldemo

threadlocal

threadlocal;

public

static

void

main

(string[

] args)

throws interruptedexception );

thread.

sleep

(100);}}}

堆記憶體的使用量呈明顯的上公升趨勢

把最後的remove方法加上之後,可以看出堆記憶體的使用量明顯降低了。

執行緒 ThreadLocal使用及資料結構

1 threadlocal的使用 threadlocal的確是資料的隔離,但是並非資料的複製,而是在每乙個執行緒中建立乙個新的資料物件,然後每乙個執行緒使用的是不一樣的。public class threadlocaltest public integer getnextstr public sta...

ORACLE 資料儲存結構之邏輯儲存結構

資料塊 data blocks 資料塊是 oracle 邏輯儲存結構中的最小的邏輯單位,也是執行資料庫輸入輸出操作的最小儲存單位。oracle 資料存放在 oracle 資料塊 中,而不是 作業系統塊 中。通常 oracle 資料塊是作業系統塊的整數倍,如果作業系統快的大小為 2048b 並且ora...

ORACLE 資料儲存結構之物理儲存結構

資料檔案 資料檔案是用於儲存使用者應用程式資料和oracle系統內部資料的檔案,這些檔案在作業系統中就是普通的作業系統檔案,oracle在建立表空間的同時會建立資料檔案。oracle資料庫在邏輯上由表空間組成,每個表空間可以包含乙個或多個資料檔案,乙個資料檔案只能隸屬於乙個表空間。控制檔案 控制檔案...