Java對象內存佈局之謎

一個Java對象在堆上除了成員信息,還有其餘內容嗎?他在堆上是如何佈局的?接下來本文將以Hotspot爲例分析Java對象內存佈局之謎。算法

堆中的Java對象

在Hotspot中一個Java對象包含以下三個部分:數組

  1. 對象頭
  2. 實例信息
  3. 對齊信息

對象頭

對象頭要分兩種類型:數據結構

  • 普通對象包含:Mark Word、Klass Pointer
  • 數組對象包含:Mark Word、Klass Pointer、Array Length

不一樣類型JVM下,對象頭每一部分佔用內存大小jvm

數據類型 32位JVM(bit) 64位JVM(bit) 開啓指針壓縮的64位JVM(bit)
Mark Word 32 64 64
Klass Pointer 32 64 32
Array Length 32 32 32

可見在64位JVM中開啓指針壓縮(-XX:UseCompressedOops)後, JVM只是針對類型指針(Klass Pointer)進行壓縮。而數組長度無論在什麼類型的JVM裏都是32bit。ide

不一樣類型JVM下,對象頭佔用內存大小佈局

數據類型 32位JVM(bit) 64位JVM(bit) 開啓指針壓縮的64位JVM(bit)
普通對象 64 128 96
數組對象 96 160 128

因而可知,對象頭仍是比較耗空間的。那麼用了這麼多內存,對象頭具體都存放了寫什麼信息呢?線程

mark word

mark word裏存放的是對象運行時的信息,不一樣狀態的對象裏mark word 存放的信息是不一樣的。具體內容可看下錶:設計

32位JVM指針

存儲內容(30bit) 鎖狀態(2bit)
identify_hashcode:25 | age:4 | biased_lock:1 (01)無鎖
threadId:23 | age:4 | epoch:2 | biased_lock:1 (01)偏向鎖
ptr_to_lock_record:30 (00)輕量級鎖
ptr_to_heavyweight_monitor:30 (10)重量級鎖
gc_info:30 (11)GC標記

64位JVMcode

存儲內容(62bit) 鎖狀態(2bit)
unused:25 | identify_hashcode:25 | unused:1 | age:4 | biased_lock:1 (01)無鎖
threadId:54 | epoch:2 | unused:1 | age:4 | biased_lock:1 (01)偏向鎖
ptr_to_lock_record:62 (00)輕量級鎖
ptr_to_heavyweight_monitor:62 (10)重量級鎖
gc_info:62 (11)GC標記
  1. 名詞解釋:

    • age: GC分代年齡
    • identify_hashcode: 對象的hashcode值
    • threadId: 偏向線程的Id
    • biased_lock: 是不是偏向鎖,由於只佔一個bit,因此只有0和1
    • epoch: 偏向時間戳
    • ptr_to_lock_record: 指向棧中輕量級鎖記錄的指針
    • ptr_to_heavyweight_monitor:指向棧中重量級鎖的指針
    • GC標記: 用於GC算法對對象的標記
    • gc_info: GC算法給不一樣狀態的標記信息
  2. 爲何要這麼實現?

    1. 由於對象頭信息是跟對象自身定義的數據結構無關的。這些信息所記錄的狀態是用於JVM對對象的管理的。更重要的是,不一樣狀態的存儲內容基本上是互斥的。因此基於節省空間的角度考慮,Mark Word 被設計成動態的。
  3. identify_hashcode 既然有方法能夠生成爲何要放在對象頭裏?

    1. 當一個對象的hashCode()未被重寫時,調用這個方法會返回一個由隨機數算法生成的值。由於一個對象的hashCode不可變,因此須要存到對象頭中。當再次調用該方法時,會直接返回對象頭中的hashcode。
    2. identify_hashcode 採用延遲加載的方式生成。只有調用hashcode()時,纔會寫入對象頭。若一個類的hashCode()方法被重寫,對象頭中將不存儲hashcode信息,由於通常咱們本身實現的hashcode()並未將生成的值寫入對象頭。
  4. 當對象的狀態不是默認狀態時,對象的hashcode去哪兒了?

    1. 當是輕量級鎖/重量級鎖時,jvm會將對象的 mark word 複製一份到棧幀的Lock Record中。 等線程釋放該對象時,再從新複製給對象。
    2. 若是一個對象頭中存在hashcode,則沒法使用偏向鎖。

Klass Pointer

類型指針存放的是該對象對應的類的指針。即該指針應該指向方法區的內存區域。

Array Length

數組長度只在數組類型的對象中存在。用於記錄數組的長度。避免獲取數組長度時,動態計算。以空間換時間。

實例信息

該部分存儲了一個類定義的全部的數據類型信息,包含從父類中繼承的信息。

分配策略

  • 相同寬度的字段放在一塊兒
  • 父類的字段在前,子類的字段在後
  • 若設置CompactFields=true,則子類窄類型的變量也可能插入到父類的變量的空隙中

對齊信息

因爲HotSpot規定對象的大小必須是8的整數倍,而對象頭恰好是8的整數倍,若是對象實例數據這部分不是的話,就須要佔位符對齊填充。

參考

  • <<深刻理解Java虛擬機: JVM高級特性與最佳實踐>>
相關文章
相關標籤/搜索