Java對象頭詳解


由於Java面向對象的思想,在JVM中需要大量存儲對象,存儲時為了實現一些額外的功能,需要在對象中添加一些標記字段用於增強對象功能,這些標記字段組成了對象頭。

1.對象頭形式

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

1.1.普通對象

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

1.2.數組對象

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

2.對象頭的組成

2.1.Mark Word

這部分主要用來存儲對象自身的運行時數據,如hashcode、gc分代年齡等。mark word的位長度為JVM的一個Word大小,也就是說32位JVM的Mark word為32位,64位JVM為64位。
為了讓一個字大小存儲更多的信息,JVM將字的最低兩個位設置為標記位,不同標記位下的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位的鎖狀態標記位,由於希望用盡可能少的二進制位表示盡可能多的信息,所以設置了lock標記。該標記的值不同,整個mark word表示的含義不同。

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

biased_lock:對象是否啟用偏向鎖標記,只占1個二進制位。為1時表示對象啟用偏向鎖,為0時表示對象沒有偏向鎖。
age:4位的Java對象年齡。在GC中,如果對象在Survivor區復制一次,年齡增加1。當對象達到設定的閾值時,將會晉升到老年代。默認情況下,並行GC的年齡閾值為15,並發GC的年齡閾值為6。由於age只有4位,所以最大值為15,這就是-XX:MaxTenuringThreshold選項最大值為15的原因。
identity_hashcode:25位的對象標識Hash碼,采用延遲加載技術。調用方法System.identityHashCode()計算,並會將結果寫到該對象頭中。當對象被鎖定時,該值會移動到管程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

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

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

當然,也不是所有的指針都會壓縮,一些特殊類型的指針JVM不會優化,比如指向PermGen的Class對象指針(JDK8中指向元空間的Class對象指針)、本地變量、堆棧元素、入參、返回值和NULL指針等。

2.3.array length

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

附:對象頭相關的資料

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

 
 
23人點贊
 
 
"小禮物走一走,來簡書關注我"
還沒有人贊賞,支持一下
Hypercube 不想當架構師的程序員不是好的產品經理 閱文集團碼農
總資產213 (約15.87元)共寫了6.9W字獲得436個贊共908個粉絲
 
 
全部評論 7只看作者
按時間倒序
按時間正序
 
5樓 03.02 22:53
jdk8

UseCompressedOops 和 UseCompressedClassPointers 應該是默認開啟的。
UseCompressedOops 關閉的情況下,UseCompressedClassPointers 就算強制開啟也無效,這個時候object指針和 對象類型指針都是8bytes。
UseCompressedOops 開啟的情況下,UseCompressedClassPointers 是可以關閉的,這個時候object指針是4bytes大小,對象類型指針大小是8bytes(在UseCompressedClassPointers關閉的情況下)
回復
 
 
4樓 2019.09.09 17:38
”64位的JVM將會比32位的JVM多耗費50%的內存“這句話多耗費50%是怎么計算的?
回復
 
Hypercube 作者
2019.09.11 21:36
實際測試出來的,可以創建100萬個integer對象算算或者直接測試
回復
 
添加新評論
 
3樓 2019.09.09 17:20
麻煩告訴下這個內容是從什么書上摘下來的呢?
回復
 

Hypercube 作者
2019.09.11 21:33
最后附上的參考資料
回復
 
添加新評論
 
2樓 2019.07.29 20:50
不太理解這個epoch 偏向時間戳是個干嘛的
回復
 

Hypercube 作者
2019.07.31 14:23
https://juejin.im/post/5c17964df265da6157056588 可以看看這篇文章,詳細的解釋我再去看看代碼
回復
 
添加新評論
 

 



作者:Hypercube
鏈接:https://www.jianshu.com/p/3d38cba67f8b
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM