相关文章:
对象在堆内存中的存储布局可以划分为三个部分:对象头 (Header)、实例数据 (Instance Data) 和对齐填充 (Padding)
一、对象头 (Header)
-
Mark Word
-
Mark Word 用于存储对象自身的运行时数据,如:哈希码 (HashCode)、GC 分代年龄、锁状态标志、线程持有的锁、偏向线程 ID、偏向时间戳等
-
由于对象头信息是与对象自身定义的数据无关的额外存储成本,考虑到 Java 虚拟机的空间使用效率,Mark Word 被设计成一个非固定的动态数据结构,以便在极小的空间内存储尽量多的信息,它会根据对象的状态服用自己的存储空间
-
源码注释如下,参考地址 --> markOop.hpp
-
32 位 Mark Word
-
64 位 Mark Word
-
-
类型指针
- 类型指针即对象指向它的类型元数据的指针,虚拟机通过这个指针来确定该对象是哪个类的实例
-
数组长度
-
当存储的是普通 Java 对象时,数组长度是非必需的,虚拟机可以通过普通 Java 对象的元数据信息来确定普通 Java 对象的大小
-
当存储的是数组对象时,数组长度是必需的,因为如果数组长度不确定的话,虚拟机将无法通过数组对象的元数据来确定数组对象的大小
-
二、实例数据 (Instance Data)
-
实例数据包含了对象真正存储的有效信息,是我们在代码中定义的各种类型的字段内容
-
实例数据的存储顺序会受到虚拟机分配策略参数
-XX:FieldsAllocationStyle
和字段在代码中定义顺序的影响
三、对齐填充 (Padding)
-
对齐填充不是必然存在的,也没有特别的含义,仅仅起着占位符的作用
-
虚拟机自动内存管理系统要求对象其实地址必须是 8 字节的整数倍,也就是说任何对象的大小都必须是 8 字节的整数倍
-
对象头部分已经被精心设计成正好是 8 字节的整数倍 (1 倍或者 2 倍),因此,如果对象实例数据部分没有对齐的话,就需要通过对齐填充来补全
四、归纳总结
-
对象的内存布局划分
- 对象头、实例数据、对齐填充
-
对象头
-
Mark Word
- 用于存储对象自身的运行时数据,如:哈希码 (HashCode)、GC 分代年龄、锁状态标志、线程持有的锁、偏向线程 ID、偏向时间戳等
-
类型指针
- 指向对象元数据的指针,虚拟机通过这个指针来确定该对象是哪个类的实例
-
数组长度
- 当存储的是数组对象时,数组长度必须存在,如果没有数组长度,虚拟机将无法通过数组对象的元数据来确定数组对象的大小
-
-
实例数据
- 包含了对象真正存储的有效信息
-
对齐填充
- 不是必然存在,仅起到占位符的作用