Java对象布局解析与实战

背景

对于了解Java内存布局的Coder来说,Java对象内存布局主要由头信息+对象变量(引用或原生类型)+对齐组成,而对于数组会添加一个数组长度字段,主要由头信息+对象变量(引用或原生类型)+对齐组成。而对于对象头信息,包含了众多信息,包括synchronized的锁信息、类指针信息等,下面内容主要针对Java对象头信息,做详细的分析。

对象头信息

针对不同的操作系统,会有不同的对象头信息,下面分页32位操作系统和64位操作系统的不同布局

32位虚拟机

|----------------------------------------------------------------------------------------|--------------------|
|                                    Object Header (64 bits)                             |        State       |
|-------------------------------------------------------|--------------------------------|--------------------|
|                  Mark Word (32 bits)                  |      Klass Word (32 bits)      |                    |
|-------------------------------------------------------|--------------------------------|--------------------|
| identity_hashcode:25 | age:4 | biased_lock:1 | lock:2 |      OOP to metadata object    |       Normal       |
|-------------------------------------------------------|--------------------------------|--------------------|
|  thread:23 | epoch:2 | age:4 | biased_lock:1 | lock:2 |      OOP to metadata object    |       Biased       |
|-------------------------------------------------------|--------------------------------|--------------------|
|               ptr_to_lock_record:30          | lock:2 |      OOP to metadata object    | Lightweight Locked |
|-------------------------------------------------------|--------------------------------|--------------------|
|               ptr_to_heavyweight_monitor:30  | lock:2 |      OOP to metadata object    | Heavyweight Locked |
|-------------------------------------------------------|--------------------------------|--------------------|
|                                              | lock:2 |      OOP to metadata object    |    Marked for GC   |
|-------------------------------------------------------|--------------------------------|--------------------|

从表格中可以看出,对于32位虚拟机来说,头信息包含了64位,8字节的信息。其中32位作为Mark Word,32位作为Class Word,Class Word表示当前类的类型信息,比如java.lang.Object、java.lang.Integer等信息的类信息引用地址。而Mark Word的信息有多种的变化,主要依赖于当前状态。

对于Normal状态(即无锁状态)(biased_lock = 0, lock = 01)

  • identity_hashcode是一个对象的hash值,这个值被填充的比较晚,当对对象第一次调用System.identityHashCode (obj)时,这个hash值将会被计算,并写入到对象头信息中。在其他状态的情况下,多线程竞争此对象时,identity_hashcode将不再存储在对象头中,而是存储在对象监听器中。
  • age字段表示对象在年轻代的年龄,由4位表示,最大可表示15,所以一个对象最多经历15此垃圾回收,若没有被清理,则进入到老年代。
  • biased_lock表示偏向锁标记,1表示偏向锁启用,0表示未启用

锁状态

从Jdk1.6开始默认开启偏向锁,可以通过,-XX: -UseBiasedLocking关闭

头信息中lock字段有一小表示

状态
00轻量级锁
01无锁或偏向锁
10重量级锁
11标记垃圾回收
  1. Biased状态(biased_lock=1, lock=01),即开启偏向锁
    1. thread字段,在偏向锁状态下,这个对象主要被某一个特定的线程锁持有,这个字段存储这个特定线程的id
    2. epoch字段,表示偏向时间戳
  2. Lightweight Locked状态( lock=00),即轻量级锁状态
    1. 在这种状态下,假设没有不同线程的竞争这个对象锁,或竞争并不明显。这种情况下,避免使用操作系统级别的锁,而是采用JVM提供的原子操作。
    2. ptr_to_lock_record 指向线程栈中锁记录的指针,通过CAS方式进行设置,在竞争不明显的情况下,效率高于操作系统级别的锁。
    3. 这种场景下,采用最小的系统阻塞时间,大约10毫秒左右,保证原子操作不会进入休眠状态,而是等待一个很小的周期,并且一旦资源被释放,原子周期就会结束,并立即获取锁对象。
  3. Heavyweight Locked状态(lock=10),即重量级锁状态
    1. ptr_to_heavyweight_monitor,如果对象锁争抢比较明显,将会由轻量级锁退化成重量级锁。prt_to_heavyweight_monitor指向重量级锁Monitor,依赖Mutex操作系统的互斥加锁
  4. Marked for GC状态(lock=11),即标记待垃圾回收

64位虚拟机

|------------------------------------------------------------------------------------------------------------|--------------------|
|                                            Object Header (128 bits)                                        |        State       |
|------------------------------------------------------------------------------|-----------------------------|--------------------|
|                                  Mark Word (64 bits)                         |    Klass Word (64 bits)     |                    |
|------------------------------------------------------------------------------|-----------------------------|--------------------|
| unused:25 | identity_hashcode:31 | unused:1 | age:4 | biased_lock:1 | lock:2 |    OOP to metadata object   |       Normal       |
|------------------------------------------------------------------------------|-----------------------------|--------------------|
| thread:54 |       epoch:2        | unused:1 | age:4 | biased_lock:1 | lock:2 |    OOP to metadata object   |       Biased       |
|------------------------------------------------------------------------------|-----------------------------|--------------------|
|                       ptr_to_lock_record:62                         | lock:2 |    OOP to metadata object   | Lightweight Locked |
|------------------------------------------------------------------------------|-----------------------------|--------------------|
|                     ptr_to_heavyweight_monitor:62                   | lock:2 |    OOP to metadata object   | Heavyweight Locked |
|------------------------------------------------------------------------------|-----------------------------|--------------------|
|                                                                     | lock:2 |    OOP to metadata object   |    Marked for GC   |
|------------------------------------------------------------------------------|-----------------------------|--------------------|

对于64位虚拟机,不开启头指针压缩的情况下,对象头信息包含128位,即16字节。

64位虚拟机,开启头指针压缩

|--------------------------------------------------------------------------------------------------------------|--------------------|
|                                            Object Header (96 bits)                                           |        State       |
|--------------------------------------------------------------------------------|-----------------------------|--------------------|
|                                  Mark Word (64 bits)                           |    Klass Word (32 bits)     |                    |
|--------------------------------------------------------------------------------|-----------------------------|--------------------|
| unused:25 | identity_hashcode:31 | cms_free:1 | age:4 | biased_lock:1 | lock:2 |    OOP to metadata object   |       Normal       |
|--------------------------------------------------------------------------------|-----------------------------|--------------------|
| thread:54 |       epoch:2        | cms_free:1 | age:4 | biased_lock:1 | lock:2 |    OOP to metadata object   |       Biased       |
|--------------------------------------------------------------------------------|-----------------------------|--------------------|
|                         ptr_to_lock_record                            | lock:2 |    OOP to metadata object   | Lightweight Locked |
|--------------------------------------------------------------------------------|-----------------------------|--------------------|
|                     ptr_to_heavyweight_monitor                        | lock:2 |    OOP to metadata object   | Heavyweight Locked |
|--------------------------------------------------------------------------------|-----------------------------|--------------------|
|                                                                       | lock:2 |    OOP to metadata object   |    Marked for GC   |
|--------------------------------------------------------------------------------|-----------------------------|--------------------|

对于64位虚拟机,开启头指针压缩的情况下,对象头信息包含96位,即12字节。

实战展示

借助openjdk提供的jol工具,展示对象头信息,相应maven依赖如下

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

实例代码如下:

public class JavaObjectHeaderTest {

    final Object object = new Object();

    public static void main(String[] args) throws InterruptedException {
        JavaObjectHeaderTest javaHeader = new JavaObjectHeaderTest();
        javaHeader.testObject();
    }

    public void testObject() throws InterruptedException {
        System.identityHashCode(object);
        for (int i = 0; i < 1; i++) {
            new Thread(() -> {
                synchronized (object) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName());                    System.out.println(ClassLayout.parseInstance(object).toPrintable());
                }
            }, "thread" + i).start();
        }
        Thread.sleep(10000);
    }

}

更改循环的次数,将会输出不同的头信息

在不使用synchronized的情况下,输入结果:
在这里插入图片描述

添加synchronized,循环10次的情况
在这里插入图片描述

使用2次循环的情况
在这里插入图片描述

  1. 在不添加synchronized的情况,从第一行数据看出,是无锁操作
  2. 在使用了synchronized的情况,从第一行数据看出,使用了重量级锁
  3. 第三张图,从第一行数据看出,展示了使用了偏向锁

总结

  1. 在32位和64位操作系统中,对象头信息占用的字节数不同
  2. 32位操作系统,对象头信息占用64位,8字节
  3. 64位操作系统,在不开启头指针压缩的情况下,占用128位,16字节;开启头指针压缩的情况下,占用96位,12字节
  4. 头指针信息主要分为Mark Word和Class Word
  5. 根据状态位的不同,头指针Mark Word的信息不同
  6. Mark Work最后3位表示状态信息,最后两位表示加锁状态,前一位表示是否开启偏向锁
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值