在JVM虚拟机中,对象的储存布局可以为分为三个部分,对象头(Object Header)、实例数据(Instance Data)和对齐填充(Padding)。
下面我们来具体说一下各个部分。
对象头(Object Header)虚拟机的头部分主要包括存储对象自身的运行时数据(Mark Word) 和 类型指针,在数组对象中这两个的基础上会多一个数组长度。这个是对象头包含的内容。
Mark WordMark Word是用于存储对象自身的运行时数据。在32位和64位系统(未开启压缩指针)中不一样的。在对象不同的锁状态下,也是不同的。下面这幅图指明Mark Word的内容:
这个是我手画的,大家可以自己动手画一画。
类型指针(Klass Word)Klass Word即对象指向它的类型元数据的指针,Java虚拟机通过这个指针来确定该对象是哪个类的实例。
如果对象是一个Java数组,对象头中还会添加一块用于记录数组长度的数据。
实例数据(Instance Data)实例数据部分是对象真正存储的有效信息(我们在程序中定义的各中类型的字段内容),这些内容会被记录在实例数据中。
对齐填充(padding)对齐填充仅仅起到一个占位符的作用,自动内存管理系统要求起始地址必须是8字节的整数倍。对其填充就是将对象填充至8字节的整数倍。
实验现在我们需要导入一个依赖,提供支持
org.openjdk.jol jol-core0.9
然后我们来使用 ClassLayout.parseInstance(a).toPrintable() 来查看对象的布局:
查看空Object的布局
public static void main(String[] args) { Object o = new Object(); System.out.println(ClassLayout.parseInstance(o).toPrintable()); }
首先有对象头的Mark Word有64bit,8bytes,然后有4bytes的类型指针,因为是空Object所以没有实例数据,然后对齐填充4bytes。所以总结得Object对象是16bytes。
接着我们创建一个对象,来看看它的布局
首先创建一个没有属性的对象
public static void main(String[] args) { B b = new B(); System.out.println(ClassLayout.parseInstance(b).toPrintable()); } class B { }
这里和创建一个Object是一样的,就不说了。
为B对象添加基本类型的属性
public static void main(String[] args) { B b = new B(); System.out.println(ClassLayout.parseInstance(b).toPrintable()); } class B { boolean b = true; int anInt = 1; int bIn = 2; }
为对象添加基本类型的属性,对象布局中会出现实例数据这一块,并且实例数据是连续的。并且除了布尔数据占1bytes外其他的数据类型都占4bytes。
为B对象添加非基本类型的数据
public static void main(String[] args) { B b = new B(); System.out.println(ClassLayout.parseInstance(b).toPrintable()); } class B { boolean b = true; int anInt = 1; int bIn = 2; String s = "aaaa"; JsonController jsonController = new JsonController(); }
为B对象填充非基本数据类型后,在实例数据模块中会出现Padding gap,这里是将每一个数据填充到4bytes,并且非基本类型的数据也是4bytes。
总结一下就是padding gap出现必须有两个条件:实例数据不够4bytes,对象中存在非基本类型数据属性。
最后我们看一下数组对象
public static void main(String[] args) { int[] a = new int[100]; System.out.println(ClassLayout.parseInstance(a).toPrintable()); }
这是一个初始化为100长度的数组对象,我们可以看到在数组对象的对象头中多一个4bytes的记录数组长度的模块,并且在后面已经为数组开辟好一个每长度为4bytes的内存空间。这里的对象刚好是8bytes的倍数,所以就不需要对齐。
对象的内存布局就差不多讲到这里了,总结一下
对象内存布局主要是有Mark Word(一般情况下12bytes,数组对象16bytes)、实例数据(看对象是怎么样的决定)、对齐填充(对齐为8bytes的倍数)组成的。
感谢大家的观看,大家一起进步!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)