JAVA對象頭詳解(含32位虛擬機與64位虛擬機)


為什么要學習Java對象頭

學習Java對象頭主要是為了解synchronized底層原理,synchronized鎖升級過程,Java並發編程等。

JAVA對象頭

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

在HotSpot虛擬機中,對象在內存中存儲的布局可以分為3塊區域:對象頭(Header),實例數據(Instance Data)和對齊填充(Padding)。
也就是說 JAVA對象 = 對象頭 + 實例數據 + 對象填充。

其中,對象頭由兩部分組成,一部分用於存儲自身的運行時數據,稱之為 Mark Word,另外一部分是類型指針,及對象指向它的類元數據的指針。

對象頭 = Mark Word + 類型指針
(未開啟指針壓縮的情況下)
在32位系統中,Mark Word = 4 bytes = 32 bits,對象頭 = 8 bytes = 64 bits;
在64位系統中,Mark Word = 8 bytes = 64 bits ,對象頭 = 16 bytes = 128bits;

bytes 是字節,bits 是位。所以說,在32位JVM虛擬機系統中,Mark Word部分,占了4個字節,Klass Word部分也占了4個字節,所以,對象頭大小為8個字節。在64位JVM虛擬機系統中,Mark Word部分,占了8個字節,Klass Word部分也占了8個字節,所以,對象頭大小為16個字節。

32位虛擬機對象頭

32位虛擬機普通對象的對象頭

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

32位虛擬機數組對象的對象頭

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

32位虛擬機對象頭詳情如下

|--------------------------------------------------------------------------------------------------------------|
|                                          Object Header(64bits)                                               |
|--------------------------------------------------------------------------------------------------------------|
|                    Mark Word(32bits)                           |  Klass Word(32bits)    |      State         |
|--------------------------------------------------------------------------------------------------------------|
|     hashcode:25                   | age:4 | biased_lock:0 | 01 | OOP to metadata object |      Nomal         |
|--------------------------------------------------------------------------------------------------------------|
|     thread:23           | epoch:2 | age:4 | biased_lock:1 | 01 | OOP to metadata object |      Biased        |
|--------------------------------------------------------------------------------------------------------------|
|     ptr_to_lock_record:30                                 | 00 | OOP to metadata object | Lightweight Locked |
|--------------------------------------------------------------------------------------------------------------|
|     ptr_to_heavyweight_monitor:30                         | 10 | OOP to metadata object | Heavyweight Locked |
|--------------------------------------------------------------------------------------------------------------|
|                                                           | 11 | OOP to metadata object |    Marked for GC   |
|--------------------------------------------------------------------------------------------------------------|

64位虛擬機對象頭

|--------------------------------------------------------------------------------------------------------------|
|                                          Object Header(128bits)                                              |
|--------------------------------------------------------------------------------------------------------------|
|                                Mark Word(64bits)               |  Klass Word(64bits)    |      State         |
|--------------------------------------------------------------------------------------------------------------|
| unused:25|identity_hashcode:31|unused:1|age:4|biase_lock:0| 01 | OOP to metadata object |      Nomal         |
|--------------------------------------------------------------------------------------------------------------|
| thread:54|      epoch:2       |unused:1|age:4|biase_lock:1| 01 | OOP to metadata object |      Biased        |
|--------------------------------------------------------------------------------------------------------------|
|                     ptr_to_lock_record:62                 | 00 | OOP to metadata object | Lightweight Locked |
|--------------------------------------------------------------------------------------------------------------|
|                    ptr_to_heavyweight_monitor:62          | 10 | OOP to metadata object | Heavyweight Locked |
|--------------------------------------------------------------------------------------------------------------|
|                                                           | 11 | OOP to metadata object |    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的指針。

如何查看對象頭

查看對象頭,就需要用到借助JOL工具。

<dependency>
     <groupId>org.openjdk.jol</groupId>
     <artifactId>jol-core</artifactId>
     <version>0.10</version>
</dependency>

import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.vm.VM;
import static java.lang.System.out;

public class TestBiased {
    public static void main(String[] args) {
        Dog d= new Dog();
        //打印JVM的詳細信息
        out.println(VM.current().details());
        //打印對應的對象頭信息
        out.println(ClassLayout.parseInstance(a).toPrintable());
    }
}

class Dog {
}

打印,大概如下。

21:46:08.204 c.TestBiased [main] - # Running 64-bit HotSpot VM.
# Objects are 8 bytes aligned.
# Field sizes by type: 8, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 8, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

21:46:08.215 TestBiased [main] - cn.itcast.test.Dog object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                VALUE
      0     4        (object header)            01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)            00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)            50 c8 e1 3f (01010000 11001000 11100001 00111111) (1071761488)
     12     4        (object header)            71 01 00 00 (01110001 00000001 00000000 00000000) (369)
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

如果打印不一樣,可以加VM options參數-XX:-UseCompressedOops,關閉指針壓縮。

要看懂VALUE這串數還得了解一下小端存儲

一個對象創建時:
如果開啟了偏向鎖(默認開啟),那么對象創建后,markword 值為 0x05 即最后 3 位為 101,這時它的
thread、epoch、age 都為 0
偏向鎖是默認是延遲的,不會在程序啟動時立即生效,如果想避免延遲,可以加 VM 參數
-XX:BiasedLockingStartupDelay=0 來禁用延遲
如果沒有開啟偏向鎖,那么對象創建后,markword 值為 0x01 即最后 3 位為 001,這時它的 hashcode、
age 都為 0,第一次用到 hashcode 時才會賦值

參考

memory-efficient-java
深入理解Java的對象頭mark word
Java對象頭詳解
java校招面試
Java對象結構與鎖實現原理及MarkWord詳解
干掉面試官1-synchronized底層原理(從Java對象頭說到即時編譯優化)
探究java對象頭——大端存儲與小端存儲


免責聲明!

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



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