深入理解JVM干货教学 - 【Java对象结构】
创始人
2025-05-29 06:20:36

🔥深入理解JVM干货教学 - 【Java对象结构】

1、Java检测工具(JOL)

在讲述Java 对象结构之前, 我这边先引入一个我们常用的Java对象工具jol-core,jol-core
工具主要是:检查 JVM 中对象的内存布局。

1.1、工具使用(JOL)

第一步引入Maven包

org.openjdk.joljol-core0.10

第二步创建 一个简单Bean 文件,打印当前类的对象信息


#创建一个简单类
public class NormalBean {
}
#执行public static void main(String[] args) {System.System.out.println(VM.current().details());System.System.out.println("-----------------------------------------");System.err.println(ClassLayout.parseInstance(NormalBean.class).toPrintable());}

执行后 输出结果

----------------------------------结果输出-----------------------------# Running 64-bit HotSpot VM.
# Using compressed oop with 3-bit shift.
# Using compressed klass with 3-bit shift.
# WARNING | Compressed references base/shifts are guessed by the experiment!
# WARNING | Therefore, computed addresses are just guesses, and ARE NOT RELIABLE.
# WARNING | Make sure to attach Serviceability Agent to get the reliable addresses.
# Objects are 8 bytes aligned.
# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
-----------------------------------------java.lang.Class object internals:OFFSET  SIZE                                              TYPE DESCRIPTION                               VALUE0     4                                                   (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)4     4                                                   (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)8     4                                                   (object header)                           df 03 00 f8 (11011111 00000011 00000000 11111000) (-134216737)12     4                     java.lang.reflect.Constructor Class.cachedConstructor                   null16     4                                   java.lang.Class Class.newInstanceCallerCache              null20     4                                  java.lang.String Class.name                                null24     4                                                   (alignment/padding gap)                  28     4                       java.lang.ref.SoftReference Class.reflectionData                      null32     4   sun.reflect.generics.repository.ClassRepository Class.genericInfo                         null36     4                                java.lang.Object[] Class.enumConstants                       null40     4                                     java.util.Map Class.enumConstantDirectory               null44     4                    java.lang.Class.AnnotationData Class.annotationData                      null48     4             sun.reflect.annotation.AnnotationType Class.annotationType                      null52     4                java.lang.ClassValue.ClassValueMap Class.classValueMap                       null56    32                                                   (alignment/padding gap)                  88     4                                               int Class.classRedefinedCount                 092   404                                                   (loss due to the next object alignment)
Instance size: 496 bytes
Space losses: 36 bytes internal + 404 bytes external = 440 bytes total

1.2、结果分析

第一个命令是打印当前VM详细信息
 System.System.out.println(VM.current().details());

不难发现,输入结果中 包含两个特殊的英文
这里表示 我们当前JDK 启用 oop 压缩 模式,
oop 压缩是从 Java 7开始 的默认行为,只要最大堆大小小于 32 GB。当最大堆大小超过 32 GB 时,JVM 将自动关闭 oop 压缩。需要以不同方式管理超过 32 Gb 堆大小的内存利用率。

在这里插入图片描述

第二个命令是打印当前Bean详细信息 不难发现每个空Bean都有如下相同的部分。
在这里插入图片描述

2、Java对象结构

根据上面例子我们可以看出 Java 对象布局大致如图所示
PS : 如图是(64位系统)
在这里插入图片描述
大致可以分为 三大部分:

  • Header (对象头)
  • Data Fields (实际数据)
  • Paddings (填充对齐)

2.1、Header

在JDK源码官网地址上明确指出,对象的组成主要包含三个关键词:Mark word 、 Class word、Array Length【特殊一些】

2.1.1 Mark Word

mark word 在该实现中占用的 最小内存量:32 位平台为 4 个字节,64 位平台为 8 个字节
核心功能主要如下几点:

  • 存储移动 GCs元数据【定向信息和对象年龄】
  • 存储对象身份的HashCode
  • 存储锁的信息【同步锁、xxx锁…】

2.1.1.1 为什么要是设计这个东西?
VM 在处理对象的时候特别快,必须在特别毫秒级或者更短的时间快速的获取到一个对象的信息,这就是为什么它必须放到了第一位的原因。
mark word 是每个Java 对象必须存在的东西,它存储的是对象内公共的标记,.
就好比我们居民身份证: 标记 姓名、编号、性别等…

2.1.2 存储移动 GCs元数据【定向信息和对象年龄】

利用Demo演示 header GC 过程中 header GC年龄发生变化的情况。

第一步配置一下JVM参数 默认设置 1GB

-Xms1024m -Xmx1024m
static volatile Object sink;public static void main(String[] args) {System.out.println(VM.current().details());PrintWriter pw = new PrintWriter(System.out, true);Object o = new Object();ClassLayout layout = ClassLayout.parseInstance(o);long lastAddr = VM.current().addressOf(o);pw.printf("*** Fresh object is at %x%n", lastAddr);System.out.println(layout.toPrintable());int moves = 0;for (int i = 0; i < 100000; i++) {long cur = VM.current().addressOf(o);if (cur != lastAddr) {moves++;pw.printf("*** Move %2d, object is at %x%n", moves, cur);System.out.println(layout.toPrintable());lastAddr = cur;}// make garbagefor (int c = 0; c < 10000; c++) {sink = new Object();}}long finalAddr = VM.current().addressOf(o);pw.printf("*** Final object is at %x%n", finalAddr);System.out.println(layout.toPrintable());pw.close();}

执行结果 PS : 执行采用64位-VM 展示

*** Fresh object is at 7ad0b6980
java.lang.Object object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE0   8        (object header: mark)     0x0000000000000001 (non-biasable; age: 0)8   4        (object header: class)    0xf80001e512   4        (object alignment gap)    
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total*** Move  1, object is at 7bab20000
java.lang.Object object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE0   8        (object header: mark)     0x0000000000000009 (non-biasable; age: 1)8   4        (object header: class)    0xf80001e512   4        (object alignment gap)    
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total*** Move  2, object is at 7bd588a28
java.lang.Object object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE0   8        (object header: mark)     0x0000000000000011 (non-biasable; age: 2)8   4        (object header: class)    0xf80001e512   4        (object alignment gap)    
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total*** Move  3, object is at 7bab16870
java.lang.Object object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE0   8        (object header: mark)     0x0000000000000019 (non-biasable; age: 3)8   4        (object header: class)    0xf80001e512   4        (object alignment gap)    
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total*** Move  4, object is at 7bd59e870
java.lang.Object object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE0   8        (object header: mark)     0x0000000000000021 (non-biasable; age: 4)8   4        (object header: class)    0xf80001e512   4        (object alignment gap)    
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total*** Move  5, object is at 7bab20520
java.lang.Object object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE0   8        (object header: mark)     0x0000000000000029 (non-biasable; age: 5)8   4        (object header: class)    0xf80001e512   4        (object alignment gap)    
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total*** Move  6, object is at 7bfe20058
java.lang.Object object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE0   8        (object header: mark)     0x0000000000000031 (non-biasable; age: 6)8   4        (object header: class)    0xf80001e512   4        (object alignment gap)    
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total*** Move  7, object is at 780011030
java.lang.Object object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE0   8        (object header: mark)     0x0000000000000031 (non-biasable; age: 6)8   4        (object header: class)    0xf80001e512   4        (object alignment gap)    
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total*** Final object is at 780011030
java.lang.Object object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE0   8        (object header: mark)     0x0000000000000031 (non-biasable; age: 6)8   4        (object header: class)    0xf80001e512   4        (object alignment gap)    
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

结论分析
观察每个Value 值的确发送了变化 与此同时 age 不断+1
PS: 这里有一个点:关于后面两个age 有两次 6,是不是BUG?

PS:这个跟JVM 默认参数有关 默认情况JDK 默认-XX:InitialTenuringThreshold=7 移动7次进入老年代,感兴趣小伙伴可以自行设置该值 进行测试.
2.1.3 存储 身份的HashCode

每个 Java 对象都有一个哈希码。当没有用户定义时,将使用身份哈希码。
哈希码 两点特性:

  1. 分布离散,大部分对象来说 不同对象HashCode 不同
  2. 幂等性

利用Demo演示 哈希码引起的 markword 的变化

第一步配置一下JVM参数 默认设置 1GB

-Xms1024m -Xmx1024m
 public static void main(String[] args) {System.out.println(VM.current().details());Student stu = new Student();ClassLayout layout = ClassLayout.parseInstance(stu);System.out.println("---------原始对象 object");System.out.println(layout.toPrintable());System.out.println("hashCode: " + Integer.toHexString(stu.hashCode()));System.out.println();System.out.println("-----------获取完毕HashCode identityHashCode()");System.out.println(layout.toPrintable());}public static class A {// no fields}

执行结果 : PS : 执行采用64位-VM

---------原始对象 object
org.openjdk.jol.samples.JOLSample_15_IdentityHashCode$A object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE0   8        (object header: mark)     0x0000000000000001 (non-biasable; age: 0) ★  18   4        (object header: class)    0xf801339212   4        (object alignment gap)    
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes totalhashCode: 762efe5d  ★ 2-----------获取完毕HashCode identityHashCode()
org.openjdk.jol.samples.JOLSample_15_IdentityHashCode$A object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE0   8        (object header: mark)     0x000000762efe5d01 (hash: 0x762efe5d; age: 0)  ★ 38   4        (object header: class)    0xf801339212   4        (object alignment gap)    
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

结论

不难发现 在没有获取Hash之前 Header 对象【★标注 】,未执行之前 0x0000000000000001 执行以后 Header 发生了变化
0x000000762efe5d01 。

2.1.4 存储锁的信息 ★★
Java 程序中说锁没有一个不知道的,我们看看在Java底层结构中锁是如何被展示的。 本章节先简单说一下 锁的变化会引起 对象Header markDown 发生变化。

演示

利用Demo演示 BiasedLocking 【偏向锁】 引起header发生变化的情况。

**第一步配置一下JVM参数
在 JDK 9 之前,偏向锁 [BiasedLocking]仅在 5 秒后启用
* VM 启动后。 因此,测试最好用
* -XX:BiasedLockingStartupDelay=0 在 JDK 8 及更低版本上。 在 JDK 15 之后的版本,默认是关闭的 需要手动设置 -XX:+UseBiasedLocking. **

-Xms1024m -Xmx1024m  -XX:BiasedLockingStartupDelay=0
  public static void main(String[] args) {System.out.println(VM.current().details());final Student stu = new Student();ClassLayout layout = ClassLayout.parseInstance(stu);System.out.println("---------原始对象 object");System.out.println(layout.toPrintable());synchronized (stu) {System.out.println("---------原始对象 object 进行加锁操作");System.out.println(layout.toPrintable());}System.out.println("---------原始对象 object 进行加锁后 对象信息");System.out.println(layout.toPrintable());}public static class Student {}

执行结果

---------原始对象 object
org.openjdk.jol.samples.JOLSample_12_BiasedLocking$A object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE0   8        (object header: mark)     0x0000000000000005 (biasable; age: 0) ★8   4        (object header: class)    0xf801339212   4        (object alignment gap)    
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total---------原始对象 object 进行加锁操作
org.openjdk.jol.samples.JOLSample_12_BiasedLocking$A object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE0   8        (object header: mark)     0x00007ff9f1809005 (biased: 0x0000001ffe7c6024; epoch: 0; age: 0)★8   4        (object header: class)    0xf801339212   4        (object alignment gap)    
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total---------原始对象 object 进行加锁后 对象信息
org.openjdk.jol.samples.JOLSample_12_BiasedLocking$A object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE0   8        (object header: mark)     0x00007ff9f1809005 (biased: 0x0000001ffe7c6024; epoch: 0; age: 0)★8   4        (object header: class)    0xf801339212   4        (object alignment gap)    
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

结论

不难发现 在没有获取Hash之前 Header 对象【★标注 】,未执行之前 0x0000000000000005 执行以后 Header 发生了变化
0x00007ff9f1809005 。
PS:本章重点主要讲解 对象锁核心内容是什么,具体锁是如何在对象中升级以及转换
可以参考如下文章:Java锁的升级-干货教学

2.1.2 KLass Word

从计算机的角度,对象本身实际上一些字节数据,当我们VM 在处理每个对象的时候他需要知道每个对象是什么?

KLass Word 主要核心功能:

  1. 运行时类型检查
  2. 确定对象的大小。
  3. 确定虚拟/接口调用的目标。

参考源码 如下: Klass C源码地址

2.1.2.1 运行时类型检查

Klass 会存储 元数据,元数据中会写入 类信息、超类、以及实现接口等信息,这些信息在编译器编译过程中会直接一些流程的检查以及应用。

在这里插入图片描述

2.1.3 Array Length

除了 我们场景的对象以外 ,我们说一下 一个特殊的对象 数组。 数组 中带有一小块 元数据。 对象类型仅对数组元素类型进行编码,因此需要将数组长度存储在其他位置。
演示

我们打印一下数据

  public static void main(String[] args) {System.out.println(VM.current().details());System.out.println(ClassLayout.parseInstance(new long[0]).toPrintable());for (int size = 0; size <= 8; size++) {System.out.println(ClassLayout.parseInstance(new byte[size]).toPrintable());}}
[J object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE0   8        (object header: mark)     0x0000000000000001 (non-biasable; age: 0)8   4        (object header: class)    0xf80001a912   4        (array length)            016   0   long [J.             N/A
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total[B object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE0   8        (object header: mark)     0x0000000000000001 (non-biasable; age: 0)8   4        (object header: class)    0xf80000f512   4        (array length)            016   0   byte [B.             N/A
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total[B object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE0   8        (object header: mark)     0x0000000000000001 (non-biasable; age: 0)8   4        (object header: class)    0xf80000f512   4        (array length)            116   1   byte [B.             N/A17   7        (object alignment gap)    
Instance size: 24 bytes
Space losses: 0 bytes internal + 7 bytes external = 7 bytes total[B object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE0   8        (object header: mark)     0x0000000000000001 (non-biasable; age: 0)8   4        (object header: class)    0xf80000f512   4        (array length)            216   2   byte [B.             N/A18   6        (object alignment gap)    
Instance size: 24 bytes
Space losses: 0 bytes internal + 6 bytes external = 6 bytes total
.......................
ta
.......................
[B object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE0   8        (object header: mark)     0x0000000000000001 (non-biasable; age: 0)8   4        (object header: class)    0xf80000f512   4        (array length)            816   8   byte [B.             N/A
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
结论

随着数据不断变化 (array length) 也不断变化

2.2、实际数据以及Padding

下面用一个简单Demo 演示一下 数据如何进行填充的

演示
 public static void main(String[] args) {StudentOne student =new StudentOne();System.out.println(ClassLayout.parseInstance(student).toPrintable());}public static  class StudentOne {private long state;}
执行结果 【64位系统】
org.openjdk.jol.samples.JOLSample_Padding$StudentOne object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE0   8        (object header: mark)     0x0000000000000001 (non-biasable; age: 0)8   4        (object header: class)    0xf800c14312   4        (alignment/padding gap)   16   8   long StudentOne.state          0
Instance size: 24 bytes ★
Space losses: 4 bytes internal + 0 bytes external = 4 bytes total  ★
分析结论

我们采用64位虚拟机
我们看一下 long 本身占用 8 bytes ,它的偏移 16-8=8bytes ,Header:一共 8+4=12 bytes
一共才 12+8=20 bytes
但是是写显示 24 bytes ,为什么呢?

原因: VM 为了使这个大小成为 8 字节的倍数,JVM 添加了 4 字节的填充。

Space losses: 4 bytes internal + 0 bytes external = 4 bytes total ★

思考 能否修改对齐方式?

我们在执行之前 执行一下JVM配置测试一下 这个类

 -XX:ObjectAlignmentInBytes=16
org.openjdk.jol.samples.JOLSample_Padding$StudentOne object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE0   8        (object header: mark)     0x0000000000000001 (non-biasable; age: 0)8   4        (object header: class)    0x00060a1812   4        (alignment/padding gap)   16   8   long StudentOne.state          024   8        (object alignment gap)    
Instance size: 32 bytes
Space losses: 4 bytes internal + 8 bytes external = 12 bytes total

针对这个同一个对象 他的填充方式 按照16 bytes 进行填充,并没有截断 data 实际数据中,这样才能更好以16字节。
在这里插入图片描述

相关内容

热门资讯

再登狮城!赋能新加坡HPL物流... (来源:AGV网)Halcon Primo Logistics(HPL)是新加坡领先的端到端物流解决...
20余款故宫年货提前上架,残障... 转自:北京日报客户端距离2026年春节还有两个月,20余款故宫年货已经提前上架。除了经典福筒、福盒,...
星空有约|12月16日将迎本年... 转自:北京日报客户端12月16日将迎来本年度最小残月,届时,月球位于轨道远地点附近,人们看到的月亮像...
盐城经开区:精准助企强动能 工... 转自:扬子晚报今年以来,盐城经济开发区锚定高质量发展目标,以企业需求为导向,深耕为企服务、优化营商环...
看“天桥百戏”,北京中轴线上再... (记者 邓耀明)凛冬虽至,热情更燃,“天桥百戏”沉浸式文化体验活动之《青春之歌》行进剧正在火热进行中...