java并发编程——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这串数还得了解一下小端存储

参考

memory-efficient-java
深入理解Java的对象头mark word
Java对象头详解
java校招面试
Java对象结构与锁实现原理及MarkWord详解
干掉面试官1-synchronized底层原理(从Java对象头说到即时编译优化)
探究java对象头——大端存储与小端存储

猜你喜欢

转载自blog.csdn.net/e891377/article/details/108905401