写点什么

☕【JVM 技术之旅】深入挖掘 Java 对象的内存结构

发布于: 2021 年 05 月 25 日
☕【JVM 技术之旅】深入挖掘Java对象的内存结构

📕 每日一句

善于利用时间的人,总会拥有充分的时间

📕 基本概念

在 JVM 虚拟机种 Java 对象的内存结构如图所示分为三大块:对象头(Object Header)、实例数据(Instance Data)、对齐填充(Padding)。 对象头:标记字段、类型指针、数组长度(限于数组对象)


对象头中主要部分相关的数据大小


  • 对象头(Object header)

  • Mark Word:对象的 Mark Word 部分占 4 个字节,其内容是一系列的标记位,比如轻量级的标记位(00),偏向锁标记位(01)等等

  • Class 对象指针:Class 对象指针的大小也是 4 个字节,其指向的位置是对象对应的 Class 对象(其对应的元数据对象)的内存地址。

  • 对象实际数据:这里面包括了对象的所有成员变量,其大小由各个成员变量的大小决定,比如:byte 和 boolean 是 1 个字节,short 和 char 是 2 个字节,int 和 float 是 4 个字节,long 和 double 是 8 个字节,refrence 是 4 个字节

  • 对齐填充:最后一部分是对齐填充的字节,按 8 个字节填充



📕 对象头(Object Header)

📕 Mark Word(标记字段)

用于存储对象自身的运行时数据,如哈希码(HashCode)、GC 分代年龄、锁状态标志、线程持有的锁、偏向线程 ID 等。Mark Word 被设计成一个非固定的数据结构以便在极小的空间内存储尽量多的信息,它会根据自己的状态复用自己的存储空间

📕Klass Pointer(类型指针)

对象指向它的类数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例;

📕 Length(长度)

如果对象是一个 Java 数组那在对象头中还必须有一块用于记录数组长度的数据


因为虚拟机可以通过普通 Java 对象的元数据信息确定 Java 对象的大小,但是从数组的元数据中无法确定数组的大小


在 32 位 JVM 中,存放 Class 指针的空间大小是 4 字节,Mark Word 空间大小也是 4 字节,因此头部就是 8 字节,如果是数组就需要再加 4 字节表示数组的长度,如下表所示


在 64 位 JVM 中,开启指针压缩,头部存放 Class 指针的空间大小还是 4 字节,而 Mark Word 区域会变大,变成 8 字节,也就是头部最少为 12 字节,如下图所示

📕 实例数据(Instance Data)

  • 实例数据中存放了对象的字段信息。无论是从父类继承的,还是在子类中定义的,都保存在实例数据中。按照一定顺序存放,在满足这个顺序的条件下,父类定义的字段又会出现在子类定义的变量之前


注意:这部分数据的存储顺序会受到虚拟机分配参数(FieldAllocationStyle)和字段在 Java 源码中定义顺序的影响


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


longs/doubles、ints、shorts/chars、bytes/booleans、oop(Ordinary Object Pointers)
复制代码


  • 从分配策略中可以看出,相同宽度的字段总是被分配到一起

  • 在满足这个前提的条件下,父类中定义的变量会出现在子类之前


CompactFields = true;


如果 CompactFields 参数值为 true(默认为 true),那子类之中较窄的变量也可能会插入到父类变量的空隙之中


  • 对齐填充:对齐填充不是必然存在的,没有特别的含义,它仅起到占位符的作用。由于 HotSpot VM 的自动内存管理系统要求对象起始地址必须是 8 字节的整数倍,也就是说对象的大小必须是 8 字节的整数倍。对象头部分是 8 字节的倍数,所以当对象实例数据部分没有对齐时,就需要通过对齐填充来补全


📕 实际案例分析

注意:图中的 markdown 写错了 应该是 markword ,请见谅!。


在 HotspotJVM 中,32 位机器下,Integer 对象的大小是 int 的几倍


我们都知道在 java 语言规范已经规定了 int 的大小是 4 个字节,那么 Integer 对象的大小是多少呢?要知道一个对象的大小,那么必须需要知道对象在虚拟机中的结构是怎样的,来看看 Hotsopt 中对象在内存中的结构:



在上面这张图里面可以看出,对象在内存中的机构主要包含以下几个部分:


根据上面的图,那么我们可以得出 Integer 的对象的结构如下:



Integer 只有一个 int 类型的成员变量 value,所以其对象实际数据部分的大小是 4 个字节,然后再在后面填充 4 个字节达到 8 字节的对齐,所以可以得出 Integer 对象的大小是 16 个字节。


因此,我们可以得出 Integer 对象的大小是原生的 int 类型的 4 倍


关于对象的内存结构,需要注意数组的内存结构和普通对象的内存结构稍微不同,因为数据有一个长度 length 字段。所以在对象头后面还多了一个 int 类型的 length 字段,占 4 个字节,接下来才是数组中的数据。如下图:


图片总结

内存对象结构

对象访问方式有两种


用户头像

我们始于迷惘,终于更高水平的迷惘。 2020.03.25 加入

🏆 【酷爱计算机技术、醉心开发编程、喜爱健身运动、热衷悬疑推理的”极客狂人“】 🏅 【Java技术领域,MySQL技术领域,APM全链路追踪技术及微服务、分布式方向的技术体系等】 🤝未来我们希望可以共同进步🤝

评论

发布
暂无评论
☕【JVM 技术之旅】深入挖掘Java对象的内存结构