java對象頭分析

一:對象頭介紹

hotSpot虛擬機中,對象在內存中的存儲佈局可以分爲三塊區域:對象頭(header),實例數據(Insrance Data)和對齊填充(Padding).

HotSpot虛擬機的對象頭(Object Header)包括兩部分信息,第一部分用於存儲對象自身的運行時數據,如:哈希碼(HashCode),GC分帶年齡,鎖狀態標誌,線程持有的鎖,偏向線程ID,偏向時間戳等等,這部分數據的長度在32位和64位的虛擬機(暫不考慮開啓指針壓縮的場景)中分別爲32個和64個bits,官方稱它爲"Mark Word"。

注意:以下的都是基於64位操作系統來分析的。
下面是截取一張hotSpot源碼當中的註釋(markword的結構,定義在markOop.hpp文件)
在這裏插入圖片描述
java對象頭在不同狀態下有不同的表現形式,主要有三種狀態:無鎖狀態,加鎖狀態,gc標記狀態。

下面這兩幅圖可以更清晰的說明java中對象頭上的Mark Word的組成以及狀態。

在這裏插入圖片描述
在這裏插入圖片描述

二 證明對象頭

(1)證明基本數據類型所佔的byte(B)

public class ZPF {
    
}

public class JOLExample1 {
    static  ZPF a = new ZPF();
    public static void main(String[] args) {
        //jvm的信息
        out.println(VM.current().details());
        out.println(ClassLayout.parseInstance(a).toPrintable());
    }
}

打印出的結果如下:

# Running 64-bit HotSpot VM.
# Using compressed oop with 0-bit shift.
# Using compressed klass with 3-bit shift.
# Objects are 8 bytes aligned.
# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

com.luban.layout.ZPF 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)                           43 c1 00 20 (01000011 11000001 00000000 00100000) (536920387)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

這裏Field sizes by type: 4(跳過這個),
1, 1, 2, 2, 4, 4, 8, 8 [bytes]分別對應
boolean,byte,char,short,int,float,long,double的大小(從最後開始對應)
可以看到整個對象一共16B,其中對象頭(Object header)12B,還有4B是對齊字節。
那麼問題來了,什麼叫做對象的實例數據?對象頭裏面的12B存的到底是什麼呢?

(2)證明boolean確實佔一個byte
在這裏插入圖片描述
可以看到我只在ZPF這個實體類裏面添加了一個boolean的類型
打印結果如下:
在這裏插入圖片描述
其他的基本數據類型,你可以自己測一下。

我們可以看到整個對象是16byte,其中12個是對象頭,boolean佔1個byte,剩下3個byte是對齊填充。那麼12個B存儲的是什麼呢?

(3)對象頭中的存儲結構

oracle官網是這樣描述的:

object header
Common structure at the beginning of every GC-managed heap object. (Every oop points to an object header.) Includes fundamental information about the heap object’s layout, type, GC state, synchronization state, and identity hash code. Consists of two words. In arrays it is immediately followed by a length field. Note that both Java objects and VM-internal objects have a common object header format.

每個gc管理的堆對象開頭的公共結構。(每個oop都指向一個對象頭。)包括堆對象的佈局、類型、GC狀態、同步狀態和標識哈希碼的基本信息。由兩個詞組成。在數組中,它後面緊跟着一個長度字段。注意,Java對象和vm內部對象都有一個通用的對象頭格式。

這兩個詞是什麼呢?
mark word
The first word of every object header. Usually a set of bitfields including synchronization state and identity hash code. May also be a pointer (with characteristic low bit encoding) to synchronization related information. During GC, may contain GC state bits.

每個對象頭的第一個單詞。通常是一組位域,包括同步狀態和標識哈希碼。也可以是指向同步相關信息的指針(具有特徵的低比特編碼)。在GC期間,可能包含GC狀態位。

klass pointer
The second word of every object header. Points to another object (a metaobject) which describes the layout and behavior of the original object. For Java objects, the “klass” contains a C++ style “vtable”.
每個對象頭的第二個單詞。指向描述原始對象的佈局和行爲的另一個對象(元對象)。對於Java對象,“klass”包含一個c++風格的“vtable”。

其實就是:
在這裏插入圖片描述
這裏推理一下:其中由上圖可知markword佔8byte(64bit),所以klassword佔4byte。

那麼接下來我們就來分析,不同鎖的狀態下markword中的變化。

(4)無鎖狀態下的對象頭信息

public class ZPF {
 boolean flag = false;
}

   public static void main(String[] args) throws Exception {
       ZPF a = new ZPF();
        out.println("befor hash");
        //沒有計算HASHCODE之前的對象頭
        out.println(ClassLayout.parseInstance(a).toPrintable());
        //JVM 計算的hashcode
        out.println("jvm------------0x"+Integer.toHexString(a.hashCode()));
        //當計算完hashcode之後,我們可以查看對象頭的信息變化
        out.println("after hash");
        out.println(ClassLayout.parseInstance(a).toPrintable());

    }

打印結果如下:
在這裏插入圖片描述
在來看一下:無鎖狀態下,前64位的存儲。
在這裏插入圖片描述
注意:這裏對應的值是從後往前對應的,因爲是小端存儲。
在這裏插入圖片描述
(5)偏向鎖對象頭分析

 static ZPF a;
    public static void main(String[] args) throws Exception {
        Thread.sleep(5000);//虛擬機啓動時對偏向鎖有延時
        a= new ZPF();
        synchronized (a){
            out.println("lock ing");
            out.println(ClassLayout.parseInstance(a).toPrintable());
        }
    }

打印結果如下:
在這裏插入圖片描述
偏向鎖狀態下前64位:
在這裏插入圖片描述
(6)輕量級鎖對象頭分析

    static ZPF a;
    public static void main(String[] args) throws Exception {
        a = new ZPF();
        out.println("befre lock");
        out.println(ClassLayout.parseInstance(a).toPrintable());
        sync();
        out.println("after lock");
        out.println(ClassLayout.parseInstance(a).toPrintable());
    }

    public  static  void sync() throws InterruptedException {
        synchronized (a){
            out.println("lock ing");
            out.println(ClassLayout.parseInstance(a).toPrintable());
        }
    }

打印結果如下:

在這裏插入圖片描述
輕量級鎖前64位結構圖:
在這裏插入圖片描述

(7)重量級鎖對象頭分析

static ZPF a;
    public static void main(String[] args) throws Exception {
       
        a = new ZPF();
        out.println("befre lock");
        out.println(ClassLayout.parseInstance(a).toPrintable());//無鎖

        Thread t1= new Thread(){
            public void run() {
                synchronized (a){
                    try {
                        Thread.sleep(5000);
                        System.out.println("t1 release");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        
        t1.start();
        
        Thread.sleep(1000);
        
        out.println("t1 lock ing");
        out.println(ClassLayout.parseInstance(a).toPrintable());//輕量鎖
        sync();
        
        System.gc();
        out.println("after gc()");
        out.println(ClassLayout.parseInstance(a).toPrintable());//無鎖---gc
    }

    public  static  void sync() throws InterruptedException {
        synchronized (a){
            System.out.println("t1 main lock");
            out.println(ClassLayout.parseInstance(a).toPrintable());//重量鎖
        }
    }

打印結果如下:
在這裏插入圖片描述
重量級鎖前64位結構如下:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章