【Java对象】一文读懂 Java 对象庐山真面目及指针压缩
文章目录
- 版本及工具介绍
- Java 对象结构
- 对象头
- mark word 标记字
- mark word 标记字解析
- Lock Record
- class point 类元数据指针
- 实例数据
- 对齐填充
- 为什么需要对齐填充
- 常见 Java 数据类型对象分析
- ArrayList
- Long
- String
- Byte
- Boolean
- 其它
- 指针压缩
- 前置知识:32位操作系统为什么最多支持 4G 内存
- 从32位操作系统到64位操作系统
- 指针压缩:使用4字节指针的同时获得更大的内存
- 如何开启指针压缩
- 实现原理
- 思考
- mark word 数据字段为什么是不固定动态变化的
- mark word 是字段动态变化的,当获取锁时 hash code 等字段被存储在哪
- 个人简介
版本及工具介绍
- JDK版本:JDK 8
- Java 对象分析 Maven 插件
<dependency><groupId>org.openjdk.jol</groupId><artifactId>jol-core</artifactId><version>0.17</version></dependency>
Java 对象结构
- 一个 Java 对象由三部分组成:对象头、实例数据、对齐数据,其中对象头分为 mark word 标记字和 class point 类元数据指针。

- jol-core 是 Java Object Layout(JOL)库的一部分,它是一个用于分析Java对象内存布局的工具。JOL 允许我们深入了解Java对象的内部结构,包括字段的偏移量、大小和布局,以及对象头的信息等。这对于性能优化和调试非常有用,特别是当我们需要了解对象在内存中的布局时。
- 如何使用 jol-core 打印Java对象信息
public class Test {static final A MUTEX = new A();public static void main(String[] args) {// 打印 JVM 信息System.out.println(VM.current().details());// hashCode 懒加载,调用 hashCode() 方法时生成存储在对象头System.out.println(MUTEX.hashCode());System.out.println(ClassLayout.parseInstance(MUTEX).toPrintable());synchronized (MUTEX) {System.out.println(ClassLayout.parseInstance(MUTEX).toPrintable());}System.out.println(ClassLayout.parseInstance(MUTEX).toPrintable());}
}class A {int a = 2;
}// 输出
# VM mode: 64 bits
# Compressed references (oops): 3-bit shift
# Compressed class pointers: 3-bit shift
# Object alignment: 8 bytes
# ref, bool, byte, char, shrt, int, flt, lng, dbl
# Field sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8
# Array base offsets: 16, 16, 16, 16, 16, 16, 16, 16, 161407343478 // 对象 hashCodeconcurrency.A object internals:
OFF SZ TYPE DESCRIPTION VALUE0 8 (object header: mark) 0x00000053e25b7601 (hash: 0x53e25b76; age: 0)8 4 (object header: class) 0xf800c14312 4 int A.a 2
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total// 64位JVM mark word 占用8字节
// 64位JVM class point 元数据指针占用4字节(正常应该占用8字节,这里开启了指针压缩)
// 实例数据 int字段占用4字节
// 共计 16 字节 默认8字节对齐,不需要补齐concurrency.A object internals:
OFF SZ TYPE DESCRIPTION VALUE0 8 (object header: mark) 0x00000096d75ff7e8 (thin lock: 0x00000096d75ff7e8)8 4 (object header: class) 0xf800c14312 4 int A.a 2
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes totalconcurrency.A object internals:
OFF SZ TYPE DESCRIPTION VALUE0 8 (object header: mark) 0x00000053e25b7601 (hash: 0x53e25b76; age: 0)8 4 (object header: class) 0xf800c14312 4 int A.a 2
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
对象头
- 对象头由 mark word 标记字和 class point 类元数据指针两部分组成。
mark word 标记字
- mark word 记录了 Java 对象运行时的数据信息,如持有的锁、是否是偏向锁、锁持有线程、hashcode、分代年龄等等,32位JVM中占用4个字节,64位JVM中占用8个字节,具体字段如下所示:

mark word 标记字解析
补充知识:
大端存储(Big-Endian):数据的高字节存储在低地址中,数据的低字节存储在高地址中
小端存储(Little-Endian):数据的高字节存储在高地址中,数据的低字节存储在低地址中// 上文示例 Mark word 分析 JVM 64位
0x00000053e25b7601 (hash: 0x53e25b76; age: 0)十六进制数: 0x00000053e25b7601
二进制数: 0000 0000 0000 0000 0000 0000 0101 00111110 0010 0101 1011 0111 0110 0000 0001锁标记: 01 无锁
分代年龄:0000 age:0
hashCode: 101 0011 1110 0010 0101 1011 0111 0110 = hash: 0x53e25b76 = 十进制:14073434780x00000096d75ff7e8 (thin lock: 0x00000096d75ff7e8)十六进制数: 0x00000096d75ff7e8
二进制数: 0000 0000 0000 0000 0000 0000 1001 01101101 0111 0101 1111 1111 0111 1110 1000锁标记: 00 轻量级锁
指向线程堆栈Lock Record指针:
0000 0000 0000 0000 0000 0000 1001 0110 1101 0111 0101 1111 1111 0111 1110 10
Lock Record
- lock record 保存对象 mark word 的原始值,还包含识别哪个对象被锁的所必需的元数据。
class point 类元数据指针
- class point 类元数据指针指向方法区的instanceKlass实例(虚拟机根据该指针确认对象是哪个类的实例),32位JVM中占用4个字节,64位JVM中占用8个字节或4个字节(指针压缩)。
实例数据
- 存储对象的字段信息。(包括继承的字段)
对齐填充
- Java 对象的大小默认8字节对齐,当大小不为8的倍数时,需要进行对齐填充,如:14字节需要填充为16字节。
为什么需要对齐填充
- 对齐填充是一种以空间换时间的方案,可以提高内存的访问效率,本质是为了更加高效的利用缓存行。
示例:
CPU缓存行(Cache Line)是计算机处理器缓存的最小存储单位,一般来说,32 位系统一般为 4字节、64位系统一般为 8字节。

- 指针压缩技术也依赖 Java 对象字节对齐。
常见 Java 数据类型对象分析
ArrayList
java.util.ArrayList object internals:
OFF SZ TYPE DESCRIPTION VALUE0 8 (object header: mark) 0x0000000000000001 (non-biasable; age: 0)8 4 (object header: class) 0xf8002f3912 4 int AbstractList.modCount 316 4 int ArrayList.size 320 4 java.lang.Object[] ArrayList.elementData [(object), (object), (object), null, null, null, null, null, null, null, null, null, null, null, null, null]
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
Long
java.lang.Long object internals:
OFF SZ TYPE DESCRIPTION VALUE0 8 (object header: mark) 0x0000000000000001 (non-biasable; age: 0)8 4 (object header: class) 0xf80022c012 4 (alignment/padding gap) 16 8 long Long.value 1
Instance size: 24 bytes
Space losses: 4 bytes internal + 0 bytes external = 4 bytes total
String
java.lang.String object internals:
OFF SZ TYPE DESCRIPTION VALUE0 8 (object header: mark) 0x0000000000000001 (non-biasable; age: 0)8 4 (object header: class) 0xf80002da12 4 char[] String.value [S, t, r, i, n, g]16 4 int String.hash 020 4 (object alignment gap)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
Byte
java.lang.Byte object internals:
OFF SZ TYPE DESCRIPTION VALUE0 8 (object header: mark) 0x0000000000000005 (biasable; age: 0)8 4 (object header: class) 0xf80021eb12 1 byte Byte.value 113 3 (object alignment gap)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total
Boolean
java.lang.Boolean object internals:
OFF SZ TYPE DESCRIPTION VALUE0 8 (object header: mark) 0x0000000000000005 (biasable; age: 0)8 4 (object header: class) 0xf800209712 1 boolean Boolean.value true13 3 (object alignment gap)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total
其它
指针压缩
前置知识:32位操作系统为什么最多支持 4G 内存
- 先看一张8字节的内存:

如果需要寻址上面的所有格子:那么我们需要 2^6 次方个地址,即 6位操作系统。相同的算法我们计算32位的操作系统:
2^32 bit = 2^29 byte = 2^19 KB = 2^9 MB = 2^-1 GB = 0.5 GB实际值为0.5G,但是为什么说32位 CPU 最多支持 4G 内存呢?实际上CPU会把 8 bit(1Byte)当作一组,即最小的读取单元为 1 Byte, 因此 2^32 * 1 Byte = 4G// 实际上,能够使用的内存大小由两方面决定硬件和操作系统,操作系统指的是虚拟地址层面,而硬件指的是地址总线。
// 其它参考:https://www.zhihu.com/question/22594254/answer/42967413
从32位操作系统到64位操作系统
- 从上面我们知道32操作系统最多使用的内存为4G,随着我们开发的程序越来越复杂,32位操作系统已经不能满足我们的内存需求,我们进入了64操作系统的时代,我们可以使用的内存达到 4G * 2^32 ,但指针长度也达到了8个字节,过长的指针带来了新的问题:
1、增加了GC开销:64位对象引用需要占用更多的堆空间,留给其他数据的空间将会减少,从而加快了GC的发生,更频繁的进行GC。
2、降低缓存命中率:64位对象引用增大了,内存能缓存的oop将会更少,从而降低了缓存的效率。
指针压缩:使用4字节指针的同时获得更大的内存
如何开启指针压缩
-XX:+UseCompressedOops // 对象指针压缩
-XX:+UseCompressedClassPointers // 类元数据指针压缩// 如上示例中已开启
# Compressed references (oops): 3-bit shift
# Compressed class pointers: 3-bit shift// 64 JVM class point 占用4个字节
concurrency.A object internals:
OFF SZ TYPE DESCRIPTION VALUE0 8 (object header: mark) 0x00000053e25b7601 (hash: 0x53e25b76; age: 0)8 4 (object header: class) 0xf800c14312 4 int A.a 2
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
实现原理
// JVM 中 java对象默认8字节对齐 最大堆内存 32 GB(4G * 2^3),超过 32 GB 指针压缩将失效
-XX:ObjectAlignmentInBytes8字节对齐的情况下,地址的后三位总是为0:8 = 100016 = 1000024 = 1100032 = 10000040 = 10100048 = 11000056 = 11100064 = 100000072 = 1001000因此,在Java对象中存储时通过右移三位将3个0抹去,从内存中获取值时再通过将Java对象中的地址左移3位补0,从而实现使用4个字节获得 2^32 * 2^3 个内存地址,一个内存地址指向 1Byte 则总计32G内存(这也是为什么我们经常看到一些文章中说Java堆内存不要超过32G的原因,因为4字节指针,8字节对齐无法表示超过32内存,会关闭指针压缩,除非调整对齐字节数来扩大可访问的内存空间)。设置为16字节对齐:最大堆内存 64 GB(4G * 2^4),超过 64 GB 指针压缩将失效16 = 1000032 = 10000048 = 11000064 = 1000000
思考
mark word 数据字段为什么是不固定动态变化的
- 实现不增加对象的内存占用的情况下,支持对象锁并发和锁优化。
mark word 是字段动态变化的,当获取锁时 hash code 等字段被存储在哪
- HotSpot VM 若为偏向锁则未获取 hash code,若已获取 hash code 则不会获取偏向锁而是直接获取轻量级锁(若为偏向级锁,此时获取 hash code 则会膨胀为重量级锁),轻量级锁时 hash code 存放在 Lock Record 中,重量级锁时 hash code 存放在 ObjectMonitor 对象上。
- 注意:这里讨论的hash code都只针对identity hash code。用户自定义的hashCode()方法生成的 hash code 不会放在对象头。(Identity hash code是未被覆写的 java.lang.Object.hashCode() 或者 java.lang.System.identityHashCode(Object) 所返回的值。)
- 参考大R回答:https://www.zhihu.com/question/52116998/answer/133400077
个人简介
👋 你好,我是 Lorin 洛林,一位 Java 后端技术开发者!座右铭:Technology has the power to make the world a better place.
🚀 我对技术的热情是我不断学习和分享的动力。我的博客是一个关于Java生态系统、后端开发和最新技术趋势的地方。
🧠 作为一个 Java 后端技术爱好者,我不仅热衷于探索语言的新特性和技术的深度,还热衷于分享我的见解和最佳实践。我相信知识的分享和社区合作可以帮助我们共同成长。
💡 在我的博客上,你将找到关于Java核心概念、JVM 底层技术、常用框架如Spring和Mybatis 、MySQL等数据库管理、RabbitMQ、Rocketmq等消息中间件、性能优化等内容的深入文章。我也将分享一些编程技巧和解决问题的方法,以帮助你更好地掌握Java编程。
🌐 我鼓励互动和建立社区,因此请留下你的问题、建议或主题请求,让我知道你感兴趣的内容。此外,我将分享最新的互联网和技术资讯,以确保你与技术世界的最新发展保持联系。我期待与你一起在技术之路上前进,一起探讨技术世界的无限可能性。
📖 保持关注我的博客,让我们共同追求技术卓越。
相关文章:
【Java对象】一文读懂 Java 对象庐山真面目及指针压缩
文章目录 版本及工具介绍Java 对象结构对象头mark word 标记字mark word 标记字解析Lock Record class point 类元数据指针 实例数据对齐填充为什么需要对齐填充 常见 Java 数据类型对象分析ArrayListLongStringByteBoolean 其它指针压缩前置知识:32位操作系统为什么…...
leetcode做题笔记210. 课程表 II
现在你总共有 numCourses 门课需要选,记为 0 到 numCourses - 1。给你一个数组 prerequisites ,其中 prerequisites[i] [ai, bi] ,表示在选修课程 ai 前 必须 先选修 bi 。 例如,想要学习课程 0 ,你需要先完成课程 1…...
【深度学习 AIGC】stable diffusion webUI 使用过程,参数设置,教程,使用方法
文章目录 docker快速启动vae.ckpt或者.safetensorsCFG指数/CFG Scale面部修复/Restore facesRefinerTiled VAEClip Skipprompt提示词怎么写 docker快速启动 如果你想使用docker快速启动这个项目,你可以按下面这么操作(显卡支持CUDA11.8)。如…...
论文阅读 - Detecting Social Bot on the Fly using Contrastive Learning
目录 摘要: 引言 3 问题定义 4 CBD 4.1 框架概述 4.2 Model Learning 4.2.1 通过 GCL 进行模型预训练 4.2.2 通过一致性损失进行模型微调 4.3 在线检测 5 实验 5.1 实验设置 5.2 性能比较 5.5 少量检测研究 6 结论 https://dl.acm.org/doi/pdf/10.1145/358…...
PaddleMIX学习笔记(1)
写在前面 之前对HyperLedger的阅读没有完全结束,和很多朋友一样,同时也因为工作的需要,最近开始转向LLM方向。 国内在大模型方面生态做的最好的,目前还是百度的PaddlePaddle,所以自己也就先从PP开始看起了。 众所周知…...
【网络协议】聊聊HTTPS协议
前面的文章,我们描述了网络是怎样进行传输数据包的,但是网络是不安全的,对于这种流量门户网站其实还好,对于支付类场景其实容易将数据泄漏,所以安全的方式是通过加密,加密方式主要是对称加密和非对称加密。…...
2023.11.2事件纪念
然而造化又常常为庸人设计,以时间的流逝,来洗涤旧迹,仅以留下淡红的血色和微漠的悲哀。 回顾这次事件,最深的感触就是什么是团队的力量! 当我们看到希望快要成功的时候,大家洋溢出兴奋开心的表情,一起的欢声笑语;但看…...
Scala和Play WS库编写的爬虫程序
使用Scala和Play WS库编写的爬虫程序,该程序将爬取网页内容: import play.api.libs.ws._ import scala.concurrent.ExecutionContext.Implicits.global object BaiduCrawler {def main(args: Array[String]): Unit {val url ""val proxy…...
佳易王配件进出库开单打印进销存管理系统软件下载
用版配件进出库开单打印系统,可以有效的管理:供货商信息,客户信息,进货入库打印,销售出库打印,进货明细或汇总统计查询,销售出库明细或汇总统计查询,库存查询,客户往来账…...
【深度学习基础】专业术语汇总(欠拟合和过拟合、泛化能力与迁移学习、调参和超参数、训练集、测试集和验证集)
📢:如果你也对机器人、人工智能感兴趣,看来我们志同道合✨ 📢:不妨浏览一下我的博客主页【https://blog.csdn.net/weixin_51244852】 📢:文章若有幸对你有帮助,可点赞 👍…...
【C语言:函数栈帧的创建与销毁】
文章目录 前言一、前期准备1.寄存器2.汇编指令3.测试代码 二、解开函数栈帧的神秘面纱1.栈帧大体轮廓2.main函数栈帧的创建3.main函数内执行有效代码4.烫烫烫5.函数参数的传递6.add函数栈帧的创建7.add函数内执行有效代码8.add是如何获得参数的9. add函数栈帧的销毁10.main函数…...
怎么在C++中实现云端存储变量
随着云计算技术的快速发展,现在我们可以将数据存储在云端,以便于在不同设备和地点访问。在C中,我们也可以通过一些方法来实现这个功能。本文将详细介绍如何在C中实现云端存储变量。 首先,我们需要理解,C本身并没有直接…...
短视频矩阵营销系统工具如何助力商家企业获客?
1.批量剪辑技术研发 做的数学建模算法,数学阶乘的组合乘组形式,采用两套查重机制,一套针对素材进行查重抽帧素材,一套针对成片进行抽帧素材打分制度查重,自动滤重计入打分。 2.账号矩阵分发开发 多平台,…...
PCL 计算一个平面与包围盒体素的相交线
文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 基于之前计算的包围盒体素(PCL 包围盒体素化显示),这里使用一个平面与其进行相交,并求出与其中体素单元的相交线。 二、实现代码 //标准文件 #include <iostream> #include <thread>//PCL...
面向教育的计算机视觉和深度学习5
面向教育的计算机视觉和深度学习5 1. 好处智能内容(Smart Content)任务自动化(Task Automation)缩小技能差距(Closing Skill Gap) 2. 应用程序学生学习与福利(Student Learning and Welfare&…...
FPGA芯片内部结构
参考链接:FPGA的进阶之第二章FPGA芯片内部结构(2)...
人工智能AI创作系统ChatGPT网站系统源码+AI绘画系统支持GPT4.0/支持Midjourney局部重绘
一、前言 SparkAi创作系统是基于OpenAI很火的ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统,支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美,可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建…...
Google 开源项目风格指南
目录 C 风格指南 Objective-C 风格指南 Python 风格指南 Shell 风格指南 TypeScript 风格指南 Javascript 风格指南 HTML/CSS 风格指南 C 风格指南 C 风格指南 - 内容目录 — Google 开源项目风格指南 Objective-C 风格指南 Objective-C 风格指南 - 内容目录 — Googl…...
无限上下文,多级内存管理!突破ChatGPT等大语言模型上下文限制
目前,ChatGPT、Llama 2、文心一言等主流大语言模型,因技术架构的问题上下文输入一直受到限制,即便是Claude 最多只支持10万token输入,这对于解读上百页报告、书籍、论文来说非常不方便。 为了解决这一难题,加州伯克利…...
学习剑指jvm
一直弱,jvm 1、主要解决运行状态的线上系统突然卡死,造成系统无法访问,甚至直接内存溢出异常(Out of Memory,OOM) 2、希望解决线上JVM垃圾回收的相关问题,但无从下手。 3、新项目上线,对设置…...
P3916 图的遍历 题解(反向建图)
更好的阅读体验(博客园) 题面 P3916 图的遍历 题目描述 给出 NNN 个点,MMM 条边的有向图,对于每个点 vvv,令 A(v)A(v)A(v) 表示从点 vvv 出发,能到达的编号最大的点。现在请求出 A(1),A(2),…,A(N)A(1),…...
ABAP - SMW0实现Excel模板下载与数据上传解析全流程指南(附完整代码)
1. 为什么需要Excel模板下载与上传功能 在企业级应用开发中,Excel模板的下载与上传功能几乎是标配。想象一下这样的场景:财务部门需要每月收集各部门的预算数据,如果让每个部门直接在SAP系统里录入,操作复杂且容易出错。而提供一个…...
Java调用C/C++/Rust的5种方式:FFI vs JNI vs JNA vs JNR vs Panama——2024权威对比评测
第一章:Java外部函数接口概述与技术演进脉络Java外部函数接口(Foreign Function & Memory API),即Project Panama的核心成果,是Java平台为高效、安全地与本地代码(如C/C库)及非堆内存交互而…...
如何快速解锁网易云音乐NCM文件:ncmdumpGUI终极指南
如何快速解锁网易云音乐NCM文件:ncmdumpGUI终极指南 【免费下载链接】ncmdumpGUI C#版本网易云音乐ncm文件格式转换,Windows图形界面版本 项目地址: https://gitcode.com/gh_mirrors/nc/ncmdumpGUI 还在为网易云音乐下载的NCM格式文件无法在其他…...
AI模型版本控制:Git for ML最佳实践
当软件测试遇上AI模型迭代对于软件测试从业者而言,版本控制是保障软件质量、实现可追溯性的基石。然而,当测试对象从传统的功能模块转变为动态演进的AI模型时,版本管理的复杂性陡然增加。一个推荐模型本周表现优异,下周却因数据漂…...
基于zlmediakit的RTSP流媒体服务器嵌入式开发指南
1. 为什么选择zlmediakit作为嵌入式RTSP服务器 第一次接触流媒体开发时,我试过用FFmpeg直接搭建服务,结果被复杂的协议栈和线程管理折腾得够呛。后来发现zlmediakit这个宝藏项目,它把RTSP/RTMP/HTTP-FLV等协议封装得特别友好,特别…...
2025年深度评测:掌握Liebling主题,解锁Ghost博客的现代设计潜力
2025年深度评测:掌握Liebling主题,解锁Ghost博客的现代设计潜力 【免费下载链接】liebling Beautiful and clean Ghost theme that is easy and comfortable to use. To get the latest version please head over the releases page 👉&#…...
Python从入门到精通(第14章):迭代器与生成器
开头导语 这是本系列第14章。前面你已经用过很多次迭代器和生成器——for x in data 的背后是什么,map 返回的对象为什么不能下标访问,range 为什么不会占很多内存——这些问题的答案都在本章。通过亲手实现一个迭代器类,你会对 Python 迭代协议有清晰的认识,遇到相关错误…...
Spring Security实战:Bcrypt加密算法在用户密码存储中的正确使用姿势(附完整代码)
Spring Security实战:Bcrypt加密算法在用户密码存储中的正确使用姿势(附完整代码) 在当今数字化时代,用户密码安全已成为系统开发中最基础也最关键的一环。作为开发者,我们经常面临一个核心问题:如何在数据…...
Linux dmesg实战指南:从内核消息解析到故障排查(附实用技巧与常见问题)
1. 初识dmesg:你的Linux系统健康检查仪 刚接触Linux系统管理时,我总把dmesg当成"高级版系统日志"。直到有次服务器突然宕机,才发现这个命令简直就是系统故障的"黑匣子"。想象一下,当你的电脑突然蓝屏…...
