1. 程式人生 > >淺談Java記憶體區域

淺談Java記憶體區域

一、執行時資料區域

Java在執行時,會根據需要,將記憶體區按照如下區域劃分,分為多個部分:


其中,藍色為執行緒共享的記憶體區域,橙色為執行緒獨享的記憶體區域。

1.1、方法區

儲存了虛擬機器載入的類資訊、常量、靜態變數等資料

1.2、常量區

是方法區的一部分,常用於存放編譯期生成的各種字面量和符合引用。但是常量池具有動態性,並不要求常量一定是在編譯期才能產生,也就是併發預置入Class檔案中的常量池的內容才能放入常量區,執行期間也可能將新的常量放入其中,比如Sring類的intern方法

1.3、堆

這也算程式設計師們常提到的堆記憶體(相對於棧記憶體),此記憶體佔用空間一般是最大的,該區域的唯一用途就是存放物件例項,堆是垃圾收集器管理的主要區域。從記憶體回收的角度,可以再細分為Eden區、From Survivor區、To Survivor區等;從記憶體分配的角度看,可以分出各個執行緒私有的分配緩衝區(TLAB)。需要注意的是,堆是可以處於物理上不連續,但是邏輯上連續的空間中。

1.4、程式計數器

這是比較小的一個記憶體空間,每個執行緒獨有。它記錄了每個執行緒所執行的位元組碼的行號指示器,分支、迴圈等都需要此計數器來完成。

1.5、虛擬機器棧

執行緒獨有。該區域也就是程式設計師們常提到的棧記憶體(相對於堆記憶體)。每次呼叫方法,都會在此區域建立棧幀,棧幀包括方法的區域性變數、運算元棧、動態連結、方法出口等資訊。如果方法內呼叫方法,就會加深棧幀的深度,當達虛擬機器所允許的最大深度時,會丟擲StackOverFlowError異常。

1.6、本地方法棧

執行緒獨有。與虛擬機器棧的作用非常相似,區別只是在於是虛擬機器棧為Java方法服務,本地方法棧為Native (比如com.mics.Unsafe類的compareAndSwap方法)方法服務。

1.7、直接記憶體

直接記憶體不是Java虛擬機器規範中定義的記憶體區域。JDK1.4中引入了NIO類,引入了一種基於通道和緩衝區的I/O方式,它可以使用Native函式庫直接分配堆外記憶體,然後通過Java堆中的DirectByteBuffer物件作為這塊記憶體的引用進行操作。

二、物件的記憶體佈局

在HotSpot虛擬機器總,物件在記憶體中的佈局可以分為三塊區域:物件頭、例項資料、對齊填充。

2.1、物件頭

物件頭包含兩部分資訊:物件執行時資料、型別指標

2.1.1、物件自身的執行時資料,比如雜湊碼、GC分代年齡、鎖狀態標識,執行緒持有的鎖、偏向鎖的執行緒ID、偏向時間戳等,也稱之為“Mark Word”。在32位的HotSpot虛擬機器中,如果物件處於未鎖定狀態,Mark Word的32位空間中,25位用於雜湊碼,4位GC分代年齡,2位鎖標誌位,1位固定為0。其他狀態下則會進行擴充套件,本身Mark Word也是非固定的資料結構。

2.1.2、型別指標:通過該指標可以確定物件是哪個類的例項。然而並不是所有虛擬機器都會有型別指標,比如使用控制代碼訪問的虛擬機器就不需要。

2.2、例項資料

類中定義的各種型別的欄位內容。HotSpot中,默認同樣寬度的欄位會分配到一起,在此前提下,父類的欄位會在子類之前。

2.3、對齊填充

並非必然存在,只是起佔位的作用。HotSpot虛擬機器的記憶體管理要求物件的起始地址是8位元組的整數倍,所以當例項資料不符沒有對齊時,就需要通過對齊填充來補全。

三、物件的訪問定位

物件的訪問定位是指棧上面的reference資料來操作堆上面的具體物件。目前主流的訪問方式有使用控制代碼和直接指標兩種

3.1、使用控制代碼

Java堆中會劃分出一塊記憶體來作為控制代碼池,reference儲存的是控制代碼池的地址。控制代碼池中則包含了物件例項資料和型別資料的具體地址(可以通過此地址確定該物件的型別)

使用控制代碼的好處是穩定的控制代碼地址,當垃圾收集時,物件會被移動,此時只需要修改控制代碼池中的地址即可。

3.2、直接指標

reference中儲存的是物件在堆中的地址。最大好處就是速度快,直接節省了一次指標定位時間開銷。HotSpot是採用該方式進行物件訪問的。

四、溢位測試

待補充。


注:借鑑《深入理解Java虛擬機器》