Java 对象的内存布局

一个Java 对象在在内存中的存储布局分为3 块区域(HostSpot VM):数组

1. 对象头

对象头的信息主要包括两个部分:数据结构

  • Mark Word
  • 类型指针
  • 数组长度(若是是数组才有)

1.1 Mark Word

Mark Word 的定义:oop

Mark Word 用于存储对象自身的运行时数据,如哈希码(HashCode)、GC 分代年龄、锁状态标志、线程持有的锁、偏向线程ID 、偏向时间戳等。这部分数据的长度在32 位和64 位的虚拟机中分别为32 bit 和64 bit 。布局

问题?:若是对象须要存储的运行时数据有不少,超过了32位 或64 位Bitmap 结构所能记录的限度应该怎么办? (对象头是与对象自己无关的额外存储成本)spa

考虑到虚拟机的的空间效率,Mark Word 被设计成一个非固定的数据结构以便在极小的空间内存储尽量多的信息,它会根据对象的状态复用本身的存储空间。线程

例如在32位 的虚拟机中,若是对象处于未被锁定的状态,32 bit 的分布:设计

  • 对象哈希码:25 bit
  • GC 分代年龄:4 bit
  • 锁标志位: 2 bit
  • 1 bit 固定为 0
HotSpot 虚拟机对象头Mark Word
存储内容 标志位 状态
对象哈希码、对象分代年龄 01 未锁定
指向锁记录的指针 00 轻量级锁定
指向重量级锁的指针 10 膨胀(重量级锁定)
空,不须要记录信息 11 GC 标记
偏向线程ID 、 偏向时间戳、对象分代年龄 01 可偏向

标志位是可复用的。指针

1.2 类型指针

类型指针即对象指向它的类元数据的指针,虚拟机经过这个指针来肯定这个对象是哪一个类的实例。对象

ps: 并非全部的虚拟机实现都必须保留类型指针,或者说查找对象的元数据不必定经过类型指针。继承

就至关于reference 类型。

1.3 数组长度

若是对象是一个Java 数组,那在对象头中还必须有一块用于记录数组长度的数据,由于虚拟机能够经过普通Java 对象的元数据肯定Java 对象的大小,可是从数组的元数据没法肯定数组的大小

2. 实例数据

实例数据是对象真正存储的有效信息: 代码中定义的各类类型的字段内容(包含父类继承的和子类定义的,都须要记录下来)。 这部分的存储顺序受到虚拟机分配策略(FieldsAllocationStyle)和字段在源码中的顺序的影响。 HostSpot 的默认分配策略为:

  • longs/doubles
  • ints
  • shorts/chars
  • bytes/booleans
  • oops(Ordinary Object Pointers)

能够看出:相同宽度的字段老是被分在一块儿。 另外,父类中定义的变量会出如今子类以前。 若是CompactFields 参数值为true(默认为true),那么子类之中较窄的变量也可能会插入父类变量的空隙之中。

3. 对齐填充

对齐填充在内存布局里面仅仅起到占位符的做用,并非必然存在的,也没有特殊含义。

因为 HostSpot VM 的自动内存管理系统要求对象的起始地址必须是8字节的整数倍,换句话说对象的大小必须为8字节的整数倍,要是实例数据没有对齐,则须要进行对齐填充来补全。


参考资料:

  • 《深刻理解Java 虚拟机》
相关文章
相关标签/搜索