天天看點

Java 對象的記憶體布局

  1. 對象頭

    對象頭的資訊主要包括兩個部分:

Mark Word

類型指針

數組長度(如果是數組才有)

1.1 Mark Word

Mark Word 的定義:

Mark Word 用于存儲對象自身的運作時資料,如哈希碼(HashCode)、GC 分代年齡、鎖狀态标志、線程持有的鎖、偏向線程ID 、偏向時間戳等。這部分資料的長度在32 位和64 位的虛拟機中分别為32 bit 和64 bit 。

問題?:如果對象需要存儲的運作時資料有很多,超過了32位 或64 位Bitmap 結構所能記錄的限度應該怎麼辦? (對象頭是與對象本身無關的額外存儲成本)

考慮到虛拟機的的空間效率,Mark Word 被設計成一個非固定的資料結構以便在極小的空間記憶體儲盡可能多的資訊,它會根據對象的狀态複用自己的存儲空間。

例如在32位 的虛拟機中,如果對象處于未被鎖定的狀态,32 bit 的分布:

對象哈希碼:25 bit

GC 分代年齡:4 bit

鎖标志位: 2 bit

1 bit 固定為 0

HotSpot 虛拟機對象頭Mark Word

存儲内容

标志位

狀态

對象哈希碼、對象分代年齡

01

未鎖定

指向鎖記錄的指針

00

輕量級鎖定

指向重量級鎖的指針

10

膨脹(重量級鎖定)

空,不需要記錄資訊

11

GC 标記

偏向線程ID 、 偏向時間戳、對象分代年齡

01

可偏向

标志位是可複用的。

1.2 類型指針

類型指針即對象指向它的類中繼資料的指針,虛拟機通過這個指針來确定這個對象是哪個類的執行個體。

ps: 并不是所有的虛拟機實作都必須保留類型指針,或者說查找對象的中繼資料不一定通過類型指針。

就相當于reference 類型。

1.3 數組長度

如果對象是一個Java 數組,那在對象頭中還必須有一塊用于記錄數組長度的資料,因為虛拟機可以通過普通Java 對象的中繼資料确定Java 對象的大小,但是從數組的中繼資料無法确定數組的大小。

2. 執行個體資料

執行個體資料是對象真正存儲的有效資訊: 代碼中定義的各種類型的字段内容(包含父類繼承的和子類定義的,都需要記錄下來)。

這部分的存儲順序受到虛拟機配置設定政策(FieldsAllocationStyle)和字段在源碼中的順序的影響。

HostSpot 的預設配置設定政策為:

longs/doubles

ints

shorts/chars

bytes/booleans

oops(Ordinary Object Pointers)

可以看出:相同寬度的字段總是被分在一起。

另外,父類中定義的變量會出現在子類之前。

如果CompactFields 參數值為true(預設為true),那麼子類之中較窄的變量也可能會插入父類變量的空隙之中。

3. 對齊填充

對齊填充在記憶體布局裡面僅僅起到占位符的作用,并不是必然存在的,也沒有特殊含義。

由于 HostSpot VM 的自動記憶體管理系統要求對象的起始位址必須是8位元組的整數倍,換句話說對象的大小必須為8位元組的整數倍,要是執行個體資料沒有對齊,則需要進行對齊填充來補全。

Java 對象的記憶體布局

繼續閱讀