對象頭是個什么東東?


  對象頭,顧名思義,就是對象的頭。對象是實例化出來的,實例化的前提是必須有類這個模板。舉個不大恰當的例子,人類就是個類,你我他就是人類實例化出來的對象。我們的頭,自然就是對象頭。我們的頭有口鼻眼耳,對象頭也有一些東西,主要包含兩部分:Mark Word(標記字)和Class Pointer(類指針),如果是數組對象還得再加一項Array Length(數組長度)。

  對象頭為啥要有這些東東?Mark Word用來標記運行時信息(每個對象各不相同),Class Pointer用來指向生成該對象所在的類(認祖歸宗),Array Length告訴我們數組的長度。后兩項簡單易懂,最復雜的是這個Mark Word了,看下JVM的markWord.hpp是怎么規定它的:

// The markWord describes the header of an object.
//
// Bit-format of an object header (most significant first, big endian layout below):
//
//
32 bits: // -------- // hash:25 ------------>| age:4 biased_lock:1 lock:2 (normal object) // JavaThread*:23 epoch:2 age:4 biased_lock:1 lock:2 (biased object) // // 64 bits: // -------- // unused:25 hash:31 -->| unused_gap:1 age:4 biased_lock:1 lock:2 (normal object) // JavaThread*:54 epoch:2 unused_gap:1 age:4 biased_lock:1 lock:2 (biased object)

  它說markWord描述了對象頭,采用大端點表示,markWord包含了哈希碼(hash)、GC分代年齡(age)、偏向鎖(biased_lock)、鎖狀態(lock)和用來濫竽充數的間隔位(64位里那些unused開頭的字段)。32位和64位機器存儲這些東東的位大小各不相同。如果對象不再是普通對象(normal object),變成了一個偏向鎖(biased object),那么markWord所含內容的哈希碼就變成了持有鎖的線程指針(JavaThread*)、偏向時間戳(epoch),其他不變。

  最后再說這個鎖狀態:

//  - the two lock bits are used to describe three states: locked/unlocked and monitor.
//
//    [ptr             | 00]  locked             ptr points to real header on stack
//    [header      | 0 | 01]  unlocked           regular object header
//    [ptr             | 10]  monitor            inflated lock (header is wapped out)
//    [ptr             | 11]  marked             used to mark an object

  00——輕量鎖,存儲內容是棧幀中指向鎖對象的指針(ptr

  01——無鎖,至於是否偏向鎖,需要再往前一位看,0:無鎖,1:偏向鎖。至於所存儲之物,上面已提到:如果是普通對象(0),存儲內容就是上哈希碼GC分代年齡;若是偏向鎖(1),則是擁有鎖的線程指針偏向時間戳對象分代年齡

  10——監視器鎖(moniter),也就是重量鎖,存儲內容為指向鎖對象的指針(ptr

  11——GC標記

  好了,理論說完,下面舉例說明:

  1、pom.xml新增jar包:

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

  2、新增測試類:

import com.wlf.springcloudgateway.javabean.HelloWorld;
import org.openjdk.jol.info.ClassLayout;

import java.util.concurrent.TimeUnit;

public class ObjectMarkWord {

    public static void main(String[] args) throws InterruptedException {

        // 實例化對象
        HelloWorld helloWorld = HelloWorld.builder().name("wlf").build();

        // 1、普通對象,無鎖
        System.out.println("普通對象:");
        printfObjMarkWord(helloWorld);

        // 2、實例化數組,打印數組對象信息,無鎖
        System.out.println("數組對象:");
        String[] address = new String[]{"hangzhou", "nanjing", "shenzhen"};
        printfObjMarkWord(address);


        // 3、單線程首次加鎖,輕量鎖
        System.out.println("輕量鎖:");
        new Thread(() -> {
            synchronized (helloWorld) {
                printfObjMarkWord(helloWorld);
            }
        }).start();

        TimeUnit.SECONDS.sleep(2);

        // 4、多個線程加鎖,升級重量鎖
        System.out.println("重量鎖:");
        for (int i = 0; i < 2; i++) {
            new Thread(() -> {
                synchronized (helloWorld) {
                    printfObjMarkWord(helloWorld);
                }
            }).start();
        }
    }

    private static void printfObjMarkWord(Object obj) {
        System.out.println("-------------------------");
        System.out.println(ClassLayout.parseInstance(obj).toPrintable());
        System.out.println("-------------------------");

    }

}
import lombok.Data;

@Data
@Builder
public class HelloWorld {
    private String name;
}

 

  在跑之前給IDEA去掉指針壓縮(jdk8默認開啟):Run -> Edit Configurations -> VM:options那一欄加上-XX:-UseCompressedOops

 

 

 

  輸出結果:

普通對象:
-------------------------
com.wlf.springcloudgateway.javabean.HelloWorld 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)                           88 49 0e 17 (10001000 01001001 00001110 00010111) (386812296)
     12     4                    (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
     16     8   java.lang.String HelloWorld.name                           (object)
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

-------------------------
數組對象:
-------------------------
[Ljava.lang.String; 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)                           20 e9 dd 16 (00100000 11101001 11011101 00010110) (383641888)
     12     4                    (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
     16     4                    (object header)                           03 00 00 00 (00000011 00000000 00000000 00000000) (3)
     20     4                    (alignment/padding gap)                  
     24    24   java.lang.String String;.<elements>                        N/A
Instance size: 48 bytes
Space losses: 4 bytes internal + 0 bytes external = 4 bytes total

-------------------------
輕量鎖:
-------------------------
com.wlf.springcloudgateway.javabean.HelloWorld object internals:
 OFFSET  SIZE               TYPE DESCRIPTION                               VALUE
      0     4                    (object header)                           60 f4 05 1a (01100000 11110100 00000101 00011010) (436597856)
      4     4                    (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4                    (object header)                           88 49 0e 17 (10001000 01001001 00001110 00010111) (386812296)
     12     4                    (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
     16     8   java.lang.String HelloWorld.name                           (object)
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

-------------------------
重量鎖:
-------------------------
com.wlf.springcloudgateway.javabean.HelloWorld object internals:
 OFFSET  SIZE               TYPE DESCRIPTION                               VALUE
      0     4                    (object header)                           ca da 66 17 (11001010 11011010 01100110 00010111) (392616650)
      4     4                    (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4                    (object header)                           88 49 0e 17 (10001000 01001001 00001110 00010111) (386812296)
     12     4                    (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
     16     8   java.lang.String HelloWorld.name                           (object)
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

-------------------------
-------------------------
com.wlf.springcloudgateway.javabean.HelloWorld object internals:
 OFFSET  SIZE               TYPE DESCRIPTION                               VALUE
      0     4                    (object header)                           ca da 66 17 (11001010 11011010 01100110 00010111) (392616650)
      4     4                    (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4                    (object header)                           88 49 0e 17 (10001000 01001001 00001110 00010111) (386812296)
     12     4                    (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
     16     8   java.lang.String HelloWorld.name                           (object)
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

-------------------------

Process finished with exit code 0

 

  我本機是64位的,所以markWord、class pointer、array length都是8字節:

 

  

  有鎖、無鎖,看標黃的00、01,是否偏向鎖,看標紅的那一位。同一個對象的class pointer是一致的,從標黃處也能看出來。普通對象是無鎖(01)的,首次加鎖后變成了輕量鎖(00,為啥不是偏向鎖?參見)。接着再另起兩個線程競爭鎖,原來的輕量鎖膨脹為重量鎖。這里有個問題:為啥偏向鎖沒有出來冒泡?答案參見偏向鎖是個什么東東? 


免責聲明!

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



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