Java对象的内存布局

在JVM规范中,对象在内存中的布局依次划分为3个区域:对象头(Header),实例数据(Instance Data)以及对齐填充(Padding)。

对象头

虚拟机对象的对象头包含两部分信息或三部分信息




java对象头结构 jvm对象头_java对象头


JVM 对象头一般占用两个机器码,在 32-bit JVM 上占用 64bit, 在 64-bit JVM 上占用 128bit 即 16 bytes(暂不考虑开启压缩指针的场景)

  1. Mark Word:用于存储对象自身的运行时数据,例如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等信息。Mark Word占用一个机器码,在32位和64位的JVM中,这部分数据分别为32bit和64bit。
  2. klass pointer:用于存储对象的类型指针,该指针指向它的类元数据的指针,JVM通过这个指针来确定对象是哪个类的实例。
  3. 另外,如果对象是一个Java数组,那在对象头中还必须有一块用于记录数组长度的数据,因为虚拟机可以通过普通Java对象的元数据信息确定Java对象的大小,但是从数组的元数据中无法确定数组的大小。

下面是用一张图展示 32位JVM上对象头的内存分布,方便理解。


java对象头结构 jvm对象头_数据_02


上图可以看出分代年龄占用4bit,4bit只能存0-15,这也是年轻代年龄大于15会被移动到老年代的原因

是否偏向锁、锁标记位是类似AQS中status的共享变量,synchronize加锁实际上改变的就是对象头中这个共享变量

实例数据

实例数据部分是对象真正存储有效信息的区域,存储了代码中定义的各种字段的内容,包括从父类继承下来的字段和子类中定义的字段。 实例数据紧随对象头,为了提高存储空间的利用率,这部分数据的存储顺序会受到虚拟机分配策略参数(FieldsAllocationStyle)和字段在Java源码中定义顺序的影响。

HotSpot虚拟机默认的分配策略如下所示:

  • doubles & longs
  • ints & floats
  • shorts & chars
  • booleans & bytes
  • references

可以看出,相同宽度的字段总是被分配到一起,并且在满足这个条件的前提下,在父类中定义的字段会出现在子类字段之前。

对齐填充

对齐填充这部分不是必须存在的,这部分仅仅是起着占位符的作用。由于JVM的自动内存管理系统要求对象的起始地址必须是8字节的整数倍,换句话说,就是对象的大小必须是8字节的整数倍。而对象头部分正好是8字节的倍数(1倍或两倍)。因此当对象实例部分数据没有对齐时,就需要对剩余的部分进行填充。

查看对象布局

添加如下依赖

org.openjdk.jol      jol-core      0.9

示例类

public class A {    private int i;    private boolean b;    private long l;}public class T {    private int i;    private A a;}

使用如下代码打印对象的布局

T t = new T();        System.out.println(Integer.toHexString(t.hashCode()));//计算hashCode        String s = ClassLayout.parseInstance(t).toPrintable();        System.out.println(s);

结果如下

6b71769eorg.example.T object internals: OFFSET  SIZE            TYPE DESCRIPTION                               VALUE      0     4                 (object header)                           01 9e 76 71 (00000001 10011110 01110110 01110001) (1903599105)      4     4                 (object header)                           6b 00 00 00 (01101011 00000000 00000000 00000000) (107)      8     4                 (object header)                           48 72 06 00 (01001000 01110010 00000110 00000000) (422472)     12     4             int T.i                                       0     16     4   org.example.A T.a                                       null     20     4                 (loss due to the next object alignment)Instance size: 24 bytesSpace losses: 0 bytes internal + 4 bytes external = 4 bytes total

解释

注意

由于Intel CPU是小端存储,所以这里的字节需要倒着看。

前面3个4字节的Object header是对象头部分,后面就是Filed部分,最后加上 loss due to the next object alignment(由于下一个对象对齐而造成的损失)对齐填充。