1.对象在堆内存中布局
2.对象的对象头
3.对象的实例数据
4.对象的对齐填充
5.看看Object的对象头
6.看看自定义对象的对象头
7.总结
1.对象在堆内存中布局
当咱们写入这样一行代码
Object object = new Object();
的时候,咱们都晓得它会在咱们的JVM堆->新生区->伊甸园区新建一个对象,然而咱们可能只是晓得这个对象在哪儿,然而对这个对象的内存构造却知之甚少,明天咱们就来细说一下,JAVA对象的内存布局。
咱们先来看这样一张图:
java对象在内存中的布局分为以下三个局部:
1)对象头
2)实例数据
3)对齐填充
接下来咱们来具体介绍以下这三个局部。
2.对象的对象头
首先是对象头,这是java对象内存布局中,最重要的一个局部,它分为了两个局部:
1)对象标记Mark Word
2)类型指针(类元信息)
首先是对象标记:
默认存储的是HashCode,分代年龄,锁标记位,GC标记等信息。
这些信息是与对象本身定义无关的数据,所以MarkWork被设计成一个非固定的数据结构,以便在小的空间内可能尽量存储更多的数据。
它会依据对象的状态复用本人的存储空间,也就是说在运行期间,MarkWork里的存储对象会随着锁标记位的扭转而发生变化。
类型指针(类元信息):
对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
对象头的大小:
在64位零碎中,Mark Word占了8个字节,类型指针占了8个字节,一共是16个字节。
3.对象的实例数据
这个局部应该是咱们每天接触的局部,咱们平时写的字段都在这里,还包含父类的属性信息(如果是数组的实例局部还包含数组的长度),这部分内存依照4个字节对齐。
4.对象的对齐填充
虚拟机要求对象的起始地址必须是8字节的整数倍,填充数据不是必须存在的,如果对象头+对象的实例数据不是8的整数倍,那么对象的对齐填充就会依照8字节补充对齐。
5.看看Object的对象头
在应用java查看Object的对象头之前,咱们要先引入一个工具类,这样才能够更不便地查看对象头。
<dependency> <groupId>org.openjdk.jol</groupId> <artifactId>jol-core</artifactId> <version>0.9</version> </dependency>
接着咱们小试一下,看看咱们平时应用的Object类的对象头有几个字节:
public static void main(String[] args) { Object o = new Object(); System.out.println(ClassLayout.parseInstance(o).toPrintable()); }
发现这里的问题没有,后面咱们说,对象头占16个字节,然而它这里只占12个字节?最初一排显示了失落?所以对象头到底是占16个字节还是12个字节呢?
答案是————都对。 因为JVM默认帮咱们开启了对象头的压缩,关上这个压缩,对象头就占12位,敞开这个压缩,对象头就占16位,咱们再来试一下:
咱们先在JVM运行的时候,应用这个参数:-XX:-UseCompressedClassPointers 这个参数就是敞开压缩参数。
然再运行,取得的后果为:
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;}
咱们发现,对象实例数据区域显著多了很多自定义的字段,并且进行了对齐填充。
7.总结
明天咱们学习了JAVA对象在内存中的布局,这一部分内容是为了为解说锁降级做铺垫。