Java對象頭詳解

由于Java面向?qū)ο蟮乃枷?,在JVM中需要大量存儲對象,存儲時為了實現(xiàn)一些額外的功能,需要在對象中添加一些標記字段用于增強對象功能,這些標記字段組成了對象頭。

1.對象頭形式

JVM中對象頭的方式有以下兩種(以32位JVM為例):

1.1.普通對象

|--------------------------------------------------------------|
|                     Object Header (64 bits)                  |
|------------------------------------|-------------------------|
|        Mark Word (32 bits)         |    Klass Word (32 bits) |
|------------------------------------|-------------------------|

1.2.數(shù)組對象

|---------------------------------------------------------------------------------|
|                                 Object Header (96 bits)                         |
|--------------------------------|-----------------------|------------------------|
|        Mark Word(32bits)       |    Klass Word(32bits) |  array length(32bits)  |
|--------------------------------|-----------------------|------------------------|

2.對象頭的組成

2.1.Mark Word

這部分主要用來存儲對象自身的運行時數(shù)據(jù),如hashcode、gc分代年齡等。mark word的位長度為JVM的一個Word大小,也就是說32位JVM的Mark word為32位,64位JVM為64位。
為了讓一個字大小存儲更多的信息,JVM將字的最低兩個位設(shè)置為標記位,不同標記位下的Mark Word示意如下:

|-------------------------------------------------------|--------------------|
|                  Mark Word (32 bits)                  |       State        |
|-------------------------------------------------------|--------------------|
| identity_hashcode:25 | age:4 | biased_lock:1 | lock:2 |       Normal       |
|-------------------------------------------------------|--------------------|
|  thread:23 | epoch:2 | age:4 | biased_lock:1 | lock:2 |       Biased       |
|-------------------------------------------------------|--------------------|
|               ptr_to_lock_record:30          | lock:2 | Lightweight Locked |
|-------------------------------------------------------|--------------------|
|               ptr_to_heavyweight_monitor:30  | lock:2 | Heavyweight Locked |
|-------------------------------------------------------|--------------------|
|                                              | lock:2 |    Marked for GC   |
|-------------------------------------------------------|--------------------|

其中各部分的含義如下:
lock:2位的鎖狀態(tài)標記位,由于希望用盡可能少的二進制位表示盡可能多的信息,所以設(shè)置了lock標記。該標記的值不同,整個mark word表示的含義不同。

biased_lock lock 狀態(tài)
0 01 無鎖
1 01 偏向鎖
0 00 輕量級鎖
0 10 重量級鎖
0 11 GC標記

biased_lock:對象是否啟用偏向鎖標記,只占1個二進制位。為1時表示對象啟用偏向鎖,為0時表示對象沒有偏向鎖。
age:4位的Java對象年齡。在GC中,如果對象在Survivor區(qū)復(fù)制一次,年齡增加1。當對象達到設(shè)定的閾值時,將會晉升到老年代。默認情況下,并行GC的年齡閾值為15,并發(fā)GC的年齡閾值為6。由于age只有4位,所以最大值為15,這就是-XX:MaxTenuringThreshold選項最大值為15的原因。
identity_hashcode:25位的對象標識Hash碼,采用延遲加載技術(shù)。調(diào)用方法System.identityHashCode()計算,并會將結(jié)果寫到該對象頭中。當對象被鎖定時,該值會移動到管程Monitor中。
thread:持有偏向鎖的線程ID。
epoch:偏向時間戳。
ptr_to_lock_record:指向棧中鎖記錄的指針。
ptr_to_heavyweight_monitor:指向管程Monitor的指針。

64位下的標記字與32位的相似,不再贅述:

|------------------------------------------------------------------------------|--------------------|
|                                  Mark Word (64 bits)                         |       State        |
|------------------------------------------------------------------------------|--------------------|
| unused:25 | identity_hashcode:31 | unused:1 | age:4 | biased_lock:1 | lock:2 |       Normal       |
|------------------------------------------------------------------------------|--------------------|
| thread:54 |       epoch:2        | unused:1 | age:4 | biased_lock:1 | lock:2 |       Biased       |
|------------------------------------------------------------------------------|--------------------|
|                       ptr_to_lock_record:62                         | lock:2 | Lightweight Locked |
|------------------------------------------------------------------------------|--------------------|
|                     ptr_to_heavyweight_monitor:62                   | lock:2 | Heavyweight Locked |
|------------------------------------------------------------------------------|--------------------|
|                                                                     | lock:2 |    Marked for GC   |
|------------------------------------------------------------------------------|--------------------|

2.2.class pointer

這一部分用于存儲對象的類型指針,該指針指向它的類元數(shù)據(jù),JVM通過這個指針確定對象是哪個類的實例。該指針的位長度為JVM的一個字大小,即32位的JVM為32位,64位的JVM為64位。
如果應(yīng)用的對象過多,使用64位的指針將浪費大量內(nèi)存,統(tǒng)計而言,64位的JVM將會比32位的JVM多耗費50%的內(nèi)存。為了節(jié)約內(nèi)存可以使用選項+UseCompressedOops開啟指針壓縮,其中,oop即ordinary object pointer普通對象指針。開啟該選項后,下列指針將壓縮至32位:

  1. 每個Class的屬性指針(即靜態(tài)變量)
  2. 每個對象的屬性指針(即對象變量)
  3. 普通對象數(shù)組的每個元素指針

當然,也不是所有的指針都會壓縮,一些特殊類型的指針JVM不會優(yōu)化,比如指向PermGen的Class對象指針(JDK8中指向元空間的Class對象指針)、本地變量、堆棧元素、入?yún)ⅰ⒎祷刂岛蚇ULL指針等。

2.3.array length

如果對象是一個數(shù)組,那么對象頭還需要有額外的空間用于存儲數(shù)組的長度,這部分數(shù)據(jù)的長度也隨著JVM架構(gòu)的不同而不同:32位的JVM上,長度為32位;64位JVM則為64位。64位JVM如果開啟+UseCompressedOops選項,該區(qū)域長度也將由64位壓縮至32位。

附:對象頭相關(guān)的資料

markOop.hpp
CompressedOops
JVM優(yōu)化之壓縮普通對象指針
What is in java object header

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容