Java的对象布局和对象头以及证明

由于Java面向对象的思想,在JVM中需要大量存储对象,存储时为了实现一些额外的功能,需要在对象中添加一些标记字段用于增强对象功能,这些标记字段组成了对象头。

首先了解一下Java对象内存布局:
在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。

一、对象头

一个java对象头包含了2个word,并且好包含了堆对象的布局、类型、GC状态、同步状态和标识哈希码。

java的对象头由以下三部分组成:

1、Mark Word
2、klass Word(指向类的指针)
3、array length(数组长度)(只有数组对象才有)

在64位系统下,存放Class指针的空间大小是8字节,MarkWord是8字节,对象头为16字节。
但在64位开启指针压缩的情况下,存放Class指针的空间大小是4字节,MarkWord是8字节,对象头为12字节。

二、Mark Word

Mark Word为第一个word根据文档可以知他里面包含了锁的信息,hashcode,gc信息等等

Mark Word在32位JVM中的长度是32bit,在64位JVM中长度是64bit。
这篇文章主讲jvm64位的

Mark Word示意图如下:
在这里插入图片描述
其中各部分的含义如下:
lock:2位的锁状态标记位,由于希望用尽可能少的二进制位表示尽可能多的信息,所以设置了lock标记。该标记的值不同,整个mark word表示的含义不同。
(1)lock:2位的锁状态标记位,由于希望用尽可能少的二进制位表示尽可能多的信息,所以设置了lock标记。

(2)biased_lock:对象是否启用偏向锁标记,只占1个二进制位。为1时表示对象启用偏向锁,为0时表示对象没有偏向锁。

(3)age:4位的Java对象年龄。在GC中,如果对象在Survivor区复制一次,年龄增加1。当对象达到设定的阈值时,将会晋升到老年代。默认情况下,并行GC的年龄阈值为15,并发GC的年龄阈值为6。由于age只有4位,所以最大值为15,这就是-XX:MaxTenuringThreshold选项最大值为15的原因。

(4)identity_hashcode:25位的对象标识Hash码,采用延迟加载技术。调用方法System.identityHashCode()计算,并会将结果写到该对象头中。当对象被锁定时,该值会移动到管程Monitor中。

(5)thread:持有偏向锁的线程ID。
(6)epoch:偏向时间戳。
(7)ptr_to_lock_record:指向栈中锁记录的指针。
(8)ptr_to_heavyweight_monitor:指向管程Monitor的指针。

三、klass Word(指向类的指针)

klass Word为对象头的第二个word主要指向对象的元数据

为了节约内存默认开启s开启指针压缩

四、array length(数组长度)

如果对象是一个数组, 那在对象头中还必须有一块数据用于记录数组长度.

五、实例证明

1、添加JOL依赖:JOL来分析java的对象布局

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

2、实例

/**
 * 对象A
 */
public class A {
    //boolean flag =false;
}
/**
 * 打印Java对象头
 */
public class JoLExample {
    public static void main(String[] args) throws Exception {
        A a = new A();
        //jvm的信息
        System.out.println(VM.current().details());
        System.out.println("-------------------------");
        System.out.println(ClassLayout.parseInstance(a).toPrintable());
    }
}

3、运行结果
在这里插入图片描述

4、分析结果
1、Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]对应:[Oop(Ordinary Object Pointer), boolean, byte, char,
short, int, float, long, double]大小

整个对象一共16B,其中对象头(Object header)12B,还有4B是对齐的字节(因为在64位虚拟机上对象的大小必
须是8的倍数),由于这个对象里面没有任何字段,故而对象的实例数据为0B?
两个问题:
1、什么叫做对象的实例数据呢?
2、那么对象头里面的12B到底存的是什么呢?

首先要明白什么对象的实例数据很简单,我们可以在A当中添加一个boolean的字段,大家都知道boolean字段占
1B,然后再看结果:

在这里插入图片描述
整个对象的大小还是没有改变一共16B,其中对象头(Object header)12B,boolean字段flag(对象的实例数据)占
1B、剩下的3B就是对齐字节。
由此我们可以认为一个对象的布局大体分为三个部分分别是
1、对象头(Object header)
2、对象的实例数据
3、字节对齐

发布了143 篇原创文章 · 获赞 49 · 访问量 25万+

猜你喜欢

转载自blog.csdn.net/weixin_36586564/article/details/103633436
今日推荐