Java对象的内存布局

Java对象的内存布局

一个Java对象在内存中包括对象头(Header)、实例数据(Instance Data)和补齐填充(Padding)三个部分:
Java对象的内存布局
对象头:

  • Mark Word:用于存储对象自身的运行时数据,如哈希吗 (HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。其长度在32位系统占4字节,在64位系统中占8字节;
    Java对象的内存布局

  • Class Pointer:对象指向它的类型元数据的指针,通过该指针确定该对象是哪个类的实例。在32位系统占4字节,在64位系统中占8字节;

  • Length:如果是数组对象,还有一个保存数组长度的空间,占4个字节;

实例数据:
对象真正存储的有效信息, 即我们在程序代码里面所定义的各种类型的字段字容,无论是从父类继承下来的还是该类自身的,都需要记录下来,而这部分的存储顺序受虚拟机的分配策略参数(-XX:FieldsAllocationStyle)和字段在java源码中定义顺序的影响。

默认分配策略:long/double -> int/float -> short/char -> byte/boolean -> Reference

如果设置了-XX:FieldsAllocationStyle=0(默认是1),那么引用就会放在最前面:

Reference -> long/double -> int/float -> short/char -> byte/boolean

分配策略总是按照宽度由大到小的顺序排列,相同宽度的放在一起。在满足这个前提条件的情况下,在父类中定义的变量会出现在子类之前。如果HotSpot虚拟机的-XX: CompactFields参数值为true(默认也是true),那么子类之中较窄的变量也允许插到父类变量的空隙中,以节省一点点空间。

静态属性所占的空间通常不计算到对象本身的空间中,因为他的引用是在方法区。

对齐填充:
没有特别含义,仅起到占位符的作用。因为HotSpot虚拟机的自动内存管理系统要求对象起始地址 必须是8字节的整数倍,如果对象实例数据长度不是8字节的整数倍,对齐填充就会补全。

注意:以上对64位操作系统的描述是未开启指针压缩的情况(可通过 -XX:+UseCompressedOops 开启,jdk1.8默认开启)
64位开启指针压缩的情况下,存放Class Pointer的空间大小是4字节,MarkWord是8字节,对象头为12字节;

如果是数组对象,对象头的大小为:数组对象头8字节+数组长度4字节+对齐4字节=16字节。其中对象引用占4字节(未开启指针压缩的64位为8字节),数组MarkWord为4字节(64位未开启指针压缩的为8字节);