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

🔥深入理解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字节。
在这里插入图片描述

相关内容

热门资讯

股市必读:紫光股份(00093... 截至2025年7月25日收盘,紫光股份(000938)报收于25.04元,上涨0.64%,换手率1....
15%!美国与欧盟达成贸易协议... 据央视新闻报道,当地时间7月27日,美国总统特朗普表示,美国已与欧盟达成贸易协议,对欧盟输美商品征收...
早新闻|央行4000亿元MLF... 宏观热点 央行、农业农村部印发《关于加强金融服务农村改革 推进乡村全面振兴的意见》 近日,中国人...
21专访|细胞存储,《繁花》爷... 21世纪经济报道记者 赵云帆 上海报道 “我是一个真正意义上的创业者”,年过古稀的瞿建国,在采访中如...
华熙生物赵燕谈胶原蛋白乱象:科... 21世纪经济报道记者雷晨 北京报道 近年来,重组胶原蛋白成为医美和护肤领域的热门概念,市场宣传中不乏...
富春染织完成董事会选举换届 开... 7月25日晚间,富春染织公告显示,当日,公司2025年第一次临时股东会和富春染织第四届第一次董事会在...
圣湘生物:两款产品取得医疗器械... 每经AI快讯,圣湘生物(SH 688289,收盘价:22.94元)7月27日晚间发布公告称,圣湘生物...
10年期国债收益率升至1.73... 近期债券市场出现显著调整,多重因素交织推动收益率持续上行。权益市场强势表现与大宗商品价格上涨形成合力...
当对手都在做下沉 蜜雪冰城旗下... [ 今年5月,蜜雪集团跟巴西签署40亿元人民币的采购意向大单,其中大多数是咖啡豆。 ] 当星巴克、瑞...
新手必看!股指期货交易规则基础... 股指期货交易规则,看似复杂抽象,实则与我们的日常生活有着奇妙的共通之处。它就像一场精心编排的生活交响...
王登发履新茅台技开公司“一把手... 一则微信公众号发布的信息,披露了茅台集团旗下的技术开发公司“一把手”已换人。 近日,南都湾财社-酒水...
特斯拉机器人V3量产版亮相!马... 快科技7月27日消息,特斯拉的Optimus人形机器人V3量产版终于要来了!马斯克在最近的财报电话会...
原创 中... 在金融全球化的浪潮中,中国资本市场始终勇立潮头,不断探索前行。7月26日,中国资本市场学会成立大会暨...
报告:我国经济增长保持韧性 下... 央广网北京7月27日消息(记者 樊瑞)近日,中国金融四十人论坛(CF40论坛)发布《2025年第二季...
超6300亿元!A股银行“分红... 7月25日,成都银行完成权益分派股权登记,将于7月28日发放现金红利,这标志着A股上市银行2024年...
老铺黄金:2025年上半年单个... 7月27日晚,老铺黄金(HK06181)披露2025年中期业绩预告。预计2025年上半年实现销售业绩...
保险行业2025年上半年回顾与... 今天分享的是:保险行业2025年上半年回顾与未来展望 报告共计:59页 2025年上半年保险行业回顾...
数币App上新!消费者、商户两... 数字人民币试点持续推进,相关数字钱包手机应用程序功能也在优化中。7月21日,北京商报记者注意到,日前...
A股热点迭出,个股连续涨停!资... 近段时间以来A股市场整体走势较为强劲,上周以来在雅江概念集体上行的推动下涨势更为明显,主要指数不同程...
原创 印... 令人惊讶的是,印度人开始反思自身制造业的发展状况。印度经济学家帕纳加利亚指出,印度原本有机会在20年...