JVM(三)JVM中对象的内存布局详解

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/liupeifeng3514/article/details/79111565

在前面我们了解了Java对象在JVM中的创建过程,接下来我们再来分析一下对象在JVM中的内存布局。

在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header),实例数据( Instance Data)和对齐填充(Padding)。如下图所示(原谅我盗用了两张图。两张图是一样的,只是表达方式不一样,大家看看哪个好记吧!):

这里写图片描述

这里写图片描述
接下来我们分析其中的每一部分。

一、对象头(Header)

HotSpot 虚拟机的对象头包括两部分(非数组对象)信息,如下图所示:

这里写图片描述

  • 第一部分用于存储对象自身的运行时数据,如哈希码(HashCode)、GC 分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳、对象分代年龄,这部分信息称为“Mark Word”;Mark Word被设计成一个非固定的数据结构以便在极小的空间内存储尽量多的信息,它会根据自己的状态复用自己的存储空间;
  • 第二部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例;
  • 如果对象是一个 Java 数组,那在对象头中还必须有一块用于记录数组长度的数据。因为虚拟机可以通过普通 Java 对象的元数据信息确定Java 对象的大小,但是从数组的元数据中无法确定数组的大小。这部分数据的长度在 32 位和 64 位的虚拟机(未开启压缩指针)中分别为32bit 和 64bit。

例如,在 32 位的 HotSpot 虚拟机中,如果对象处于未被锁定的状态下,那么 Mark Word 的 32bit 空间中的 25bit 用于存储对象哈希码,4bit 用于存储对象分代年龄,2bit 用于存储锁标志位,1bit 固定为 0,如下表所示:

这里写图片描述

而在其他状态(轻量级锁定、重量级锁定、cc标记、可偏向)下对象的存储内容见下表:

这里写图片描述

代码清单2-2为HotSpot虚拟机markOop.cpp中的代码(注释)片段,它描述了32bit下Mark Word的存储状态。

这里写图片描述

二、实例数据(Instance Data)

实例数据部分是对象真正存储的有效信息,也既是我们在程序代码里面所定义的各种类型的字段内容,无论是从父类继承下来的,还是在子类中定义的都需要记录下来。

这部分的存储顺序会受到虚拟机分配策略参数(FieldsAllocationStyle)和字段在Java源码中定义顺序的影响。

HotSpot虚拟机默认的分配策略为longs/doubles、ints、shorts/chars、bytes/booleans、oops(Ordinary Object Pointers),从分配策略中可以看出,相同宽度的字段总是被分配到一起。在满足这个前提条件的情况下,在父类中定义的变量会出现在子类之前。如果 CompactFields参数值为true(默认为true),那子类之中较窄的变量也可能会插入到父类变量的空隙之中。

三、对齐填充(Padding)

对齐填充并不是必然存在的,也没有特别的含义,它仅仅起着占位符的作用。由于HotSpot VM的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说就是对象的大小必须是8字节的整数倍。对象头正好是8字节的倍数(1倍或者2倍),因此当对象实例数据部分没有对齐的话,就需要通过对齐填充来补全。


接下来我们看一个【估算对象大小】的小例子(好吧,这也是我从其他地方看来的^_^!)。

32 位系统下,当使用 new Object() 时,JVM 将会分配 8(Mark Word+类型指针) 字节的空间,128 个 Object 对象将占用 1KB 的空间。如果是 new Integer(),那么对象里还有一个 int 值,其占用 4 字节,这个对象也就是 8+4=12 字节,对齐后,该对象就是 16 字节。

以上只是一些简单的对象,那么对象的内部属性是怎么排布的?

Class A {
    int i;
    byte b;
    String str;
}

其中对象头部占用 ‘Mark Word’4 + ‘类型指针’4 = 8 字节;byte 8 位长,占用 1 字节;int 32 位长,占用 4 字节;String 只有引用,占用 4 字节;那么对象 A 一共占用了 8+1+4+4=17 字节,按照 8 字节对齐原则,对象大小也就是 24 字节。

这个计算看起来是没有问题的,对象的大小也确实是 24 字节,但是对齐(padding)的位置并不对:在 HotSpot VM 中,对象排布时,间隙是在 4 字节基础上的(在 32 位和 64 位压缩模式下)。上述例子中,int 后面的 byte,空隙只剩下 3 字节,接下来的 String 对象引用需要 4 字节来存放,因此 byte 和对象引用之间就会有 3 字节对齐,对象引用排布后,最后会有 4 字节对齐,因此结果上依然是 7 字节对齐。此时对象的结构示意图,如下图所示:

这里写图片描述

展开阅读全文

没有更多推荐了,返回首页