对象内存布局和对象头

「这是我参与11月更文挑战的第9天,活动详情查看:2021最后一次更文挑战

一 对象的(堆)内存布局

对象实例 = 对象头+实体数据+对齐填充(保证8个字节的倍数)

【如果new一个对象,但是此时对象14个字节,比8大,比16小两个字节,对齐填充会自动补齐到16】

对象头 = 对象标记Mark Word + 类元信息(又叫类型指针)。

下面图是java Object

array Object

1 对象头

对象标记包含着【哈希码,GC标记,GC次数,同步锁标记,偏向锁持有者】(每个对象都有一个hashcode)

默认存储对象的HashCode、分代年龄和锁标志位等信息。
这些信息都是与对象自身定义无关的数据,所以MarkWord被设计成一个非固定的数据结构以便在极小的空间内存存储尽量多的数据。
它会根据对象的状态复用自己的存储空间,也就是说在运行期间MarkWord里存储的数据会随着锁标志位的变化而变化。

**类元信息(class pointer):**对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。

注意:在64位系统中,Mark Word占了8个字节,类型指针占了8个字节,一共是16个字节。(默认)

2 实例数据

存放类的属性(Field)数据信息,包括父类的属性信息,
如果是数组的实例部分还包括数组的长度,这部分内存按4字节对齐。

假如类中的属性数据信息的字节数,只有21字节,那么对齐填充就会按4字节对齐,补齐到24。

3 对齐填充

将头和数据的字节数补齐用的。

整个对象的字节数 = 对象头字节数(16 默认) + 实际数据的字节数。

锁状态从上到下,从无锁到重锁,锁升级。

可以使用VM.current().details()查看虚拟机的详细信息。

下面代码可以查看当前对象的字节,头部信息等信息。

12345

public static void main(String[] args) {    Object o = new Object();    //需要引入JOL依赖    System.out.println( ClassLayout.parseInstance(o).toPrintable());}Copy
复制代码

结果:

前两个header(Mark word 8个字节),第三个header类型指针4个字节

使用上面的代码,如果引入了别的含有数据的类:

12345

class B{        int i = 25;    boolean s = false;}Copy
复制代码

这时候,实际要在上图的后面加上4字节,再加上boolean的1位字节,所以总数大概有17个字节,但是实际是需要补8倍。

17比24少7,所以补7位到24。

注意:GC年龄采用4位的存储,最大值为15,例如MaxTenuringThreshold参数的默认值就是15.

如果超过16,就会抛异常。

猜你喜欢

转载自juejin.im/post/7035264438248669197