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

相关内容

热门资讯

在大吉订购APP订购交易亏损被...   近期,不少投资者反映在“大吉订购App”上投资白银、铂金等商品遭遇严重亏损。该平台宣称提供现货订...
铂银壹号APP商品订购交易亏损...   许多中小投资者误以为网上的“商品现货订购”是合规合法的理财渠道,殊不知这背后隐藏着巨大的合规风险...
兴鑫淘金APP贵金属订购交易亏...   白银、贵金属或其他商品类投资,除国家批准的正规期货交易场所及持牌期货公司外,市面上大量第三方交易...
铂银掘金APP无资质的违规平台...   铂银掘金等平台的投资者,往往是被“注册送现金”“充值返现券”“首单免亏”等广告话术吸引,进而下载...
八方淘金APP订购交易不合规,...   “八方淘金APP”“铂银掘金APP”“铂银壹号APP”等软件被指以“现货订购”为名,实际采用高杠...
在指尖订购APP订购商品亏损被...   指尖订购APP这类平台,表面是“订购交易”,实则是披着电商外衣的非法期货陷阱。绝大多数投资者被蒙...
在犀贝易购APP做商品订购交易...   市面上打着“现货投资”“贵金属交易”旗号的APP并不少,但真正合规、受监管的平台并不多。犀贝易购...
易达淘金APP虚假商品订购交易...   易达淘金APP存在严重的虚假宣传与违规交易行为。该平台打着“商品订购”的旗号,实则通过不合规的交...
大浪淘金APP白银订购亏损被骗...   大浪淘金APP最隐蔽的套路,在于其完全封闭的内盘交易系统。表面上,它宣称与国际贵金属市场接轨;实...
一品好购APP上订购白银出现亏...   一品好购APP原名“一品订购”,运营方为上海宝智未来信息技术有限公司。近期不少投资者反映在该平台...
九州订购APP贵金属订购投资骗...   九州订购APP正通过抖.音、快手、今日头条等平台大量投放投资广告,用“5元起投”“充值返现”“0...
坚锅淘金APP交易软件并不正规...   当前,市面上大量所谓的“现货商城订购”交易软件,早已偏离了正常的实物商品买卖,演变为高杠杆、高手...
斗金订购APP贵金属期货交易骗...   斗金订购APP的投资者往往被铺天盖地的广告宣传所诱导——注册即送现金、充值返现券等看似诱人的福利...
警惕!云启购APP违规经营,披...   "5元起投、充值返现、稳赚不赔"——这些诱人的广告语背后,可能是一个精心设计的金融陷阱。  近期...
量子淘金APP贵金属投资不合规...   "4元起投""充值返券""月入过万"——这些极具诱惑力的广告语在短视频平台、社交软件上铺天盖地。...
鑫汇智投APP贵金属商品订购骗...   鑫汇智投APP非法期货投资骗局,受害者在投资过程中根本不懂交易,也不去研究和验证这个平台是否正规...
矅光智策APP平台违规经营非法...   矅光智策APP对外宣称是“商城订购”“现货交易”,但查询其经营范围,并未显示具备开展现货期货交易...
黄金珠宝怎么选?内侧刻字都代表... 潮新闻客户端 记者 马焱 通讯员 任倩影 9月8日下午,“云游杭州 乐购金秋”——“浙城·浙放心”活...
原创 冰... 最近全球国债市场正在上演魔幻一幕: 美债、日债、欧债遭遇猛烈抛售,收益率冲到几十年新高,美国财政部都...
日韩股市开盘表现分化 上证报中国证券网讯 9月9日,日韩股市开盘表现分化,韩国综合指数开盘上涨0.26%,报6972.87...