java并发编程(五)Java对象头 一、对象头 二、Mark Word简介

在我们学习synchronized的原理之前,必须要先学习下java的对象头,这有助于我们理解synchronized。

一、对象头

1.1 对象头介绍

java对象分为对象头,对象体,对齐字段,如下所示:

我们下面主要关注对象头的内容:

对象头 = Mark Word + Klass Word

如果是数组对象则:

对象头 = Mark Word + Klass Word + 数组长度

Klass Word:存储一个地址,长度取决于系统位数,32位和64位,该地址指向方法区中类的元数据信息。

常见的虚拟机分为32位和64位,则其对象头也会不同:

在32位系统中,Mark Word = 4 bytes = 32 bits,对象头 = 8 bytes = 64 bits;
在64位系统中,Mark Word = 8 bytes = 64 bits ,对象头 = 16 bytes = 128bits;

bytes 是字节,bits 是位。

  • 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位虚拟机Mark Word
|----------------------------------------------------------------------------------------------|
|                       Mark Word(32bits)                                 |        State       |
|----------------------------------------------------------------------------------------------|
|     hashcode:25                      | age:4 | biased_lock:0 |  lock:01 |      Nomal         |
|----------------------------------------------------------------------------------------------|
|     thread:23              | epoch:2 | age:4 | biased_lock:1 |  lock:01 |      Biased        |
|----------------------------------------------------------------------------------------------|
|     ptr_to_lock_record:30                                    |  lock:00 | Lightweight Locked |
|----------------------------------------------------------------------------------------------|
|     ptr_to_heavyweight_monitor:30                            |  lock:10 | Heavyweight Locked |
|----------------------------------------------------------------------------------------------|
|                                                              |  lock:11 |    Marked for GC   |
|----------------------------------------------------------------------------------------------|
  • 64位虚拟机虚拟机普通对象头
|--------------------------------------------------------------------------------------------|
|                                 Object Header(128bits)                                     |
|--------------------------------------------------------------------------------------------|
|                    Mark Word(64bits)             |         Klass Word(64bits)              | 
|--------------------------------------------------------------------------------------------|
  • 64位虚拟机虚拟机数组对象头
|---------------------------------------------------------------------------------------------------------|
|                                  Object Header (192 bits)                                               |
|--------------------------------|-----------------------|------------------------|-----------------------|
|       Mark Word(64bits)        | Klass Word(64bits)    | array length(32bits)   |  alignment(32bits)    |
|--------------------------------|-----------------------|------------------------|-----------------------|

在数组对象当中,有一个对齐长度(alignment/padding gap)是8字节,但是其中是包含数组长度4个字节,所以对齐字节长度也是4,具体可以看我下一小节的演示。

  • 64位虚拟机Mark Word
|----------------------------------------------------------------------------------------------|
|                                   Mark Word(64bits)                     |      State         |
|----------------------------------------------------------------------------------------------|
|    unused:25|identity_hashcode:31|unused:1|age:4|biase_lock:0| lock:01  |      Nomal         |
|----------------------------------------------------------------------------------------------|
|    thread:54|      epoch:2       |unused:1|age:4|biase_lock:1| lock:01  |      Biased        |
|----------------------------------------------------------------------------------------------|
|                        ptr_to_lock_record:62                 | lock:00  | Lightweight Locked |
|----------------------------------------------------------------------------------------------|
|                       ptr_to_heavyweight_monitor:62          | lock:10  | Heavyweight Locked |
|----------------------------------------------------------------------------------------------|
|                                                              | lock:11  |    Marked for GC   |
|----------------------------------------------------------------------------------------------|

1.2 查看对象头

引入下列依赖:

<!-- https://mvnrepository.com/artifact/org.openjdk.jol/jol-core -->
<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.16</version>
</dependency>

本文并没有使用网上大家都在使用的0.10版本,因为对比后我发现,新版本0.16更能清晰的体现对象头的内容。同学们可以自行对比下。

首先声明两个JVM的参数:

  • -XX:-UseCompressedOops 关闭压缩
  • -XX:+UseCompressedOops 开启压缩

我的是默认开启的,所以需要使用-XX:-UseCompressedOops将其关闭。

下面会简单模拟是普通对象和数组对象,我的电脑都是64位的,即参照64位虚拟机的结果。

给定一个空的Student对象:

public class Student {

}

测试类如下:

public class ObjectHeader {

    public static void main(String[] args) {
        //对象
        Student student = new Student();
        //数组
        String[] strings = new String[]{};
        // 打印jvm的具体参数
        System.out.println(VM.current().details());
        // 打印普通对象头信息
        System.out.println(ClassLayout.parseInstance(student).toPrintable());
        // 打印数组对象头信息
        System.out.println(ClassLayout.parseInstance(strings).toPrintable());
    }
}

结果如下:

Connected to the target VM, address: '127.0.0.1:63960', transport: 'socket'
# 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]

com.cloud.bssp.thread.objectheader.Student object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE
  0   8        (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   8        (object header: class)    0x000000001c2b1cd0
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

[Ljava.lang.String; object internals:
OFF  SZ               TYPE DESCRIPTION               VALUE
  0   8                    (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   8                    (object header: class)    0x000000001bfa4268
 16   4                    (array length)            0
 16   8                    (alignment/padding gap)   
 24   0   java.lang.String String;.<elements>        N/A
Instance size: 24 bytes
Space losses: 8 bytes internal + 0 bytes external = 8 bytes total

下面我们逐行进行讲解:

  • JVM参数
  // 64位虚拟机
  Running 64-bit HotSpot VM.
  //  按照8个字节对齐,即忽略第一个8字节
  Objects are 8 bytes aligned.
  // 忽略8字节后,余下的其实都是基本类型所占的字节:boolean,byte,char,short,int,float,long,double的大小
  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]
  • 普通对象 com.cloud.bssp.thread.objectheader.Student
// 很明显,下面是mark word的大小,8个字节,64bits
object header: mark 
// 如下是klass word的大小,8个字节,64bits
object header: class
// 实例大小是16字节。
Instance size: 16 bytes
//空间损失,是否有对其字节,内部对齐和外部对齐
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
  • 数组对象 [Ljava.lang.String; object
// 很明显,下面是mark word的大小,8个字节,64bits
object header: mark 
// 如下是klass word的大小,8个字节,64bits
object header: class
// 数组长度
array length
// 内部的对齐字节大小
alignment/padding gap
// 空数组
java.lang.String String;.<elements>        N/A
// 实例大小是24
Instance size: 24 bytes
//空间损失,8个是内部对齐字节,其中包含字节的长度4,也就是补齐了4个
Space losses: 8 bytes internal + 0 bytes external = 8 bytes total

二、Mark Word简介

这一节简单介绍小Mark Word当中的参数都是什么意思,以64位虚拟机介绍。

|----------------------------------------------------------------------------------------------|
|                                   Mark Word(64bits)                     |      State         |
|----------------------------------------------------------------------------------------------|
|    unused:25|identity_hashcode:31|unused:1|age:4|biase_lock:0| lock:01  |      Nomal         |
|----------------------------------------------------------------------------------------------|
|    thread:54|      epoch:2       |unused:1|age:4|biase_lock:1| lock:01  |      Biased        |
|----------------------------------------------------------------------------------------------|
|                        ptr_to_lock_record:62                 | lock:00  | Lightweight Locked |
|----------------------------------------------------------------------------------------------|
|                       ptr_to_heavyweight_monitor:62          | lock:10  | Heavyweight Locked |
|----------------------------------------------------------------------------------------------|
|                                                              | lock:11  |    Marked for GC   |
|----------------------------------------------------------------------------------------------|

先看state这一列,由上至下分别代表synchronized锁的状态:无锁,偏向锁,轻量级锁,重量级锁,GC。每个锁的含义将在下一篇文章重点讲解。

下面以行为单位,由上至下查看:

  • Nomal (无锁)
    unused:未使用25位
    identity_hashcode:31位的对象标识hashCode
    unused:未使用1位
    age:4位的Java对象GC回收年龄。
    biase_lock:1位偏向锁标记,0未启用
    locked:2位锁状态标志位,配合biased_lock表示java对象各阶段不同锁状态。

  • Biased(偏向锁)
    thread:54位的持有偏向锁的线程id
    epoch:偏向锁的时间戳
    unused:未使用1位
    age:4位的Java对象GC回收年龄。
    biase_lock:1位偏向锁标记,1启用
    locked:2位锁状态标志位,配合biased_lock表示java对象各阶段不同锁状态。

  • Lightweight Locked(轻量级锁)
    ptr_to_lock_record:轻量级锁状态下,指向栈中锁记录的指针。
    locked:2位锁状态标志位,00表示轻量级锁。

  • Heavyweight Locked(重量级锁)
    ptr_to_heavyweight_monitor:重量级锁状态下,指向Monitor(监视器或管程,下一篇文章讲解)的指针。
    locked:2位锁状态标志位,10表示轻量级锁。

  • Marked for GC(GC标记)
    locked:2位锁状态标志位,11表示被标记为垃圾回收。


关于对象头的内容,目前就介绍这么多,下一篇文章会讲解synchronized的相关内容和原理。有用的话给个关注,点赞吧!!

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