JAVA并发编程——Java对象内存布局和对象头

1.对象在堆内存中布局

2.对象的对象头

3.对象的实例数据

4.对象的对齐填充

5.看看Object的对象头

6.看看自定义对象的对象头

7.总结

1.对象在堆内存中布局
当我们写入这样一行代码

Object object = new Object();

的时候,我们都知道它会在我们的JVM堆->新生区->伊甸园区新建一个对象,但是我们可能只是知道这个对象在哪儿,但是对这个对象的内存结构却知之甚少,今天我们就来细说一下,JAVA对象的内存布局。

我们先来看这样一张图:
JAVA并发编程——Java对象内存布局和对象头_第1张图片
java对象在内存中的布局分为以下三个部分:
1)对象头
2)实例数据
3)对齐填充

接下来我们来详细介绍以下这三个部分。

2.对象的对象头
首先是对象头,这是java对象内存布局中,最重要的一个部分,它分为了两个部分:
1)对象标记Mark Word
2)类型指针(类元信息)

首先是对象标记:
JAVA并发编程——Java对象内存布局和对象头_第2张图片
默认存储的是HashCode,分代年龄,锁标志位,GC标记等信息。
这些信息是与对象自身定义无关的数据,所以MarkWork被设计成一个非固定的数据结构,以便在小的空间内能够尽量存储更多的数据。
它会根据对象的状态复用自己的存储空间,也就是说在运行期间,MarkWork里的存储对象会随着锁标志位的改变而发生变化

类型指针(类元信息):

JAVA并发编程——Java对象内存布局和对象头_第3张图片

对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例

对象头的大小:
在64位系统中,Mark Word占了8个字节,类型指针占了8个字节,一共是16个字节

3.对象的实例数据

这个部分应该是我们每天接触的部分,我们平时写的字段都在这里,还包括父类的属性信息(如果是数组的实例部分还包括数组的长度),这部分内存按照4个字节对齐。

4.对象的对齐填充
虚拟机要求对象的起始地址必须是8字节的整数倍,填充数据不是必须存在的,如果对象头+对象的实例数据不是8的整数倍,那么对象的对齐填充就会按照8字节补充对齐

5.看看Object的对象头
在使用java查看Object的对象头之前,我们要先引入一个工具类,这样才可以更方便地查看对象头。

        
            org.openjdk.jol
            jol-core
            0.9
        

接着我们小试一下,看看我们平时使用的Object类的对象头有几个字节:

 public static void main(String[] args) {

        Object o = new Object();

        System.out.println(ClassLayout.parseInstance(o).toPrintable());

    }

JAVA并发编程——Java对象内存布局和对象头_第4张图片

发现这里的问题没有,前面我们说,对象头占16个字节,但是它这里只占12个字节?最后一排显示了丢失?所以对象头究竟是占16个字节还是12个字节呢
答案是————都对。 因为JVM默认帮我们开启了对象头的压缩,打开这个压缩,对象头就占12位,关闭这个压缩,对象头就占16位,我们再来试一下:
我们先在JVM运行的时候,使用这个参数:-XX:-UseCompressedClassPointers 这个参数就是关闭压缩参数。

JAVA并发编程——Java对象内存布局和对象头_第5张图片

然再运行,获得的结果为:
JAVA并发编程——Java对象内存布局和对象头_第6张图片

6.看看自定义对象的对象头
Object的对象头看完了,我们看看自定义的对象头又有什么区别。

我们先自定义一个对象

class MyObj{

    private Integer id;

    private String name;

    private Integer age;

    private String address;

    private Integer status;


    private Date createTime;

    private Date updateTime;


}

我们发现,对象实例数据区域明显多了很多自定义的字段,并且进行了对齐填充。

JAVA并发编程——Java对象内存布局和对象头_第7张图片

7.总结
今天我们学习了JAVA对象在内存中的布局,这一部分内容是为了为讲解锁升级做铺垫。

你可能感兴趣的