Java 對象的內存佈局

一個Java 對象在在內存中的存儲佈局分爲3 塊區域(HostSpot VM):數組

1. 對象頭

對象頭的信息主要包括兩個部分:數據結構

  • Mark Word
  • 類型指針
  • 數組長度(若是是數組纔有)

1.1 Mark Word

Mark Word 的定義:oop

Mark Word 用於存儲對象自身的運行時數據,如哈希碼(HashCode)、GC 分代年齡、鎖狀態標誌、線程持有的鎖、偏向線程ID 、偏向時間戳等。這部分數據的長度在32 位和64 位的虛擬機中分別爲32 bit 和64 bit 。佈局

問題?:若是對象須要存儲的運行時數據有不少,超過了32位 或64 位Bitmap 結構所能記錄的限度應該怎麼辦? (對象頭是與對象自己無關的額外存儲成本)spa

考慮到虛擬機的的空間效率,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 虛擬機》
相關文章
相關標籤/搜索