深入理解JVM干货教学 - 【Java对象结构】
创始人
2025-05-29 06:20:36
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字节。
在这里插入图片描述

相关内容

热门资讯

最新或2023(历届)小学生学...  雷锋的名言  (1) 人的生命是有限的,可是,为人民服务是无限的,我要把有限的生命,投入到无限为人...
学习雷锋手抄报图片最新或202...  雷锋,原名雷正兴。1940年1月至1962年8月 在中国人民解放军某部运输连任战士、班长。1962...
最新或2023(历届)学习雷锋...  雷锋精神  雷锋精神,是以雷锋的名字命名的、以雷锋的精神为基本内涵的、在实践中不断丰富和发展着的革...
B2097 最长平台 【入门】 白细胞计数 题目描述 医院采样了某临床病例治疗期间的白细胞数量样本 nnn 份,用于分...
最新或2023(历届)学习雷锋...   向雷锋同志学习  雷锋同志因公殉职后,1963年1月7日,国防部命名他生前所在班为“雷锋班”;1...
最新或2023(历届)学雷锋手...  人物简介  雷锋,解放前是一名孤儿。解放后,在党和政府的关怀下他入学读书。参加工作后,多次当选为劳...
基础入门-算法逆向散列对称非对... 文章目录安全测试中:加密解密-识别特征&解密条件其他密文特点见:解密实例...
[Daimayuan]特殊的正... 输入nnn,输出nnn行nnn列的由+和.组成的正方形,其中最外...
最新或2023(历届)学习雷锋...  雷锋故事一:苦练  1960年1月8日,雷锋和新战士们一起,乘火车来到营口车站。这时,月台上锣鼓喧...
最新或2023(历届)学习雷锋...  钉子精神  施工任务中,他整天驾驶汽车东奔西跑,很难抽出时间学习,雷锋就把书装在挎包里,随身带在身...
最新或2023(历届)学习雷锋... 雷锋的一生虽然没有创造惊天动地的英雄伟绩,但他把自己生命的每一分热、每一分光都无私地奉献给人民,以对...
最新或2023(历届)学习雷锋...  雷锋,解放前是一名孤儿。解放后,在党和政府的关怀下他入学读书。参加工作后,多次当选为劳动模范。19...
最新或2023(历届)学习雷锋...   雷锋精神是中华民族传统美德的一种积淀,是一种随着时代进步而不断发展的与时俱进的精神。雷锋那种全心...
最新或2023(历届)学习雷锋... 中国共产主义战士雷锋在实践中表现出来的全心全意为人民服务的共产主义精神和态度。其质是:忠于共产主义事...
并发编程的特性——可见性 线程间的可见性 多线程为了提高效率,在线程本地缓存数据,造成数据修改线程...
最新或2023(历届)小学生关... 导语:近些年来,我国中东部地区雾霾天气频频发生,对我们的生活、出行产生极大的不便,也严重危害了我们的...
华为OD机试用Python实现... 华为OD机试题 本篇题目:猜密码题目输入描述输出描述:补充说明示例 1输入输出说明 编码 Code ...
移动架构44_Lifecycl... Android移动架构汇总​​​​​​​ 文章目录引子一 什么是Lifecycle?二...
最新或2023(历届)预防雾霾... 导语:下面分享一些关于预防雾霾的手抄报资料,希望对大家有所帮助!关于雾霾的手抄报图片01关于雾霾的手...
最新或2023(历届)浓浓的乡... 导语:月是故乡明。下面分享一些关于乡情的手抄报资料,希望对大家有所帮助!关于乡情的手抄报图片01关于...