对象内存存储布局概述
在JVM虚拟机中,对象的储存布局可以为分为三个部分,
对象头(Object Header)
、
实例数据(Instance Data)
和
对齐填充(Padding)
。
下面我们来具体说一下各个部分。
对象头(Object Header)
虚拟机的头部分主要包括
存储对象自身的运行时数据(Mark Word)
和
类型指针
,在数组对象中这两个的基础上会多一个
数组长度。
这个是对象头包含的内容。
Mark Word
Mark Word是用于存储对象自身的运行时数据。在32位和64位系统(未开启压缩指针)中不一样的。在对象不同的锁状态下,也是不同的。下面这幅图指明Mark Word的内容:
这个是我手画的,大家可以自己动手画一画。
类型指针(Klass Word)
Klass Word即对象指向它的类型元数据的指针,Java虚拟机通过这个指针来确定该对象是哪个类的实例。
如果对象是一个Java数组,对象头中还会添加一块用于记录数组长度的数据。
实例数据(Instance Data)
实例数据部分是对象真正存储的有效信息(我们在程序中定义的各中类型的字段内容),这些内容会被记录在实例数据中。
对齐填充(padding)
对齐填充仅仅起到一个占位符的作用,自动内存管理系统要求起始地址必须是8字节的整数倍。对其填充就是将对象填充至8字节的整数倍。
实验
现在我们需要导入一个依赖,提供支持
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.9</version>
</dependency>
然后我们来使用 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的倍数)组成的。
感谢大家的观看,大家一起进步!