該篇源自於對《深入理解java虛擬機器》的學習和總結。大牛拍磚請輕點。

1、執行時資料區域

1.1 程式計數器

定義:當前執行緒所執行的位元組碼的行號指示器。

設計目的:為了執行緒切換後能恢復到正確的執行位置,所以需要每個執行緒都擁有一個獨立的程式計數器。

注意:

a、該記憶體區域執行緒私有,各執行緒的程式計數器互不影響,獨立儲存;

b、執行的是java方法,則計數器記錄的是正在執行的指令碼的地址。執行native方法,則計數器為空;

c、是唯一一個沒有規定oom的記憶體區域。

1.2 虛擬機器棧

定義:用以描述java方法執行的記憶體模型。即每個方法在執行時都會建立一個棧幀用於儲存區域性變量表、運算元棧、動態連結,方法出口等資訊。

區域性變量表存放基本資料型別、物件引用、returnAddress型別。

注意:

a、通常說到的java中的棧指的是這裡的區域性變量表;

b、區域性變量表用到的記憶體空間在編譯時完成分配,在方法執行時期不會改變區域性變量表的大小。

1.3 本地方法棧

定義:用以描述本地方法執行的記憶體模型。

區別:與虛擬機器棧的區別在於本地方法棧為虛擬機器使用到的native方法服務,而虛擬機器棧則是為虛擬機器使用到的java方法服務。

1.4 堆

定義:所有執行緒共享,用以存放例項物件的記憶體區域。

設計目的:唯一目的是存放例項物件。

注意:

a、該記憶體區域所有執行緒共享;

b、我們常指的gc一般都是指的對堆的回收,該處的回收率是最高的。

1.5 方法區

定義:用以儲存被載入的類資訊、常量、靜態變數、即時編譯器編譯後的程式碼資料的記憶體區域。

注意:該記憶體區域所有執行緒共享。

1.5.1 執行時常量池

方法區的一部分。儲存類載入後class常量池的內容和執行時可能產生的新常量。

釋義:

class常量池指的是編譯器生成的各種字面量和符號引用。

 

2、物件

2.1 物件的建立

2.1.1 檢查

虛擬機器遇到一條new指令時,先檢測這個指令的引數能否在常量池中定位到一個類的符號引用,再檢測這個類符號引用代表的類是否被載入、解析、初始化。如果沒有,則先執行類載入過程。

2.1.2 分配記憶體

檢查完後,為物件分配記憶體(該大小在類載入完成時就能完全確定),如果記憶體規整,適合使用“記憶體碰撞”分配記憶體,否則適合使用“空閒列表”。其中記憶體是否規則由垃圾回收器是否帶有壓縮整理功能決定。

注意:

這裡的記憶體分配存在著安全問題,對併發的考慮和解決方案。

解決方案:

1、對分配記憶體空間的操作進行同步處理;

2、把記憶體分配的操作按照執行緒劃分到不同的空間中進行,即每個執行緒預先在堆中分配一塊記憶體,本地執行緒分配緩衝(TLAB),哪個執行緒要分配記憶體,在對應的TLAB中進行分配,只有TLAB用完需要再分配新的TLAB時,才進行同步處理。

2.1.3 初始化

記憶體分配完成後,將分配的記憶體空間初始化為零值。

設計目的:保證物件的例項欄位在java程式碼中可以不賦初始值就可以直接使用。

2.1.4 對物件進行設定

主要是對物件頭進行一些設定。

2.1.5 執行<init>方法

按照程式設計師的意願進行初始化操作,執行完後,才算是創建出一個真正可用的物件。

2.2 物件的記憶體佈局

2.2.1 物件頭

物件頭由兩部分資訊。

“Mark Word”:

其中一部分儲存物件的執行時資料,具體結構如圖:

型別指標:

物件指向它的類元資料指標,虛擬機器通過這個指標確定這個物件是哪個類的例項。

2.2.2 例項資料

例項資料是物件真正儲存有效資訊的區域,即程式碼中定義的各種型別的欄位內容。

注:儲存順序受虛擬機器分配策略引數和欄位在java原始碼中定義順序的影響。

2.2.3 對齊填充

不是必然存在,僅起佔位符的作用。

注意:

物件必須是8位元組的整數倍,物件頭是8位元組的整數倍,所以例項資料沒有對齊8的整數倍時,就需要對其填充來補全。

2.3 物件的訪問定位

物件訪問方式,取決於虛擬機器的實現而定。

2.3.1 控制代碼訪問

堆中劃分一塊記憶體作為控制代碼池,reference中儲存的是物件的控制代碼地址,控制代碼中包含了例項資料和型別資料各自的地址資訊。

優勢:reference中儲存的是穩定的控制代碼地址。

2.3.1 直接指標訪問

reference中直接儲存的是物件地址。

優勢:訪問速度快。