Java技术专题-JVM研究系列(24)深入挖掘Java对象的内存结构
📕 每日一句
善于利用时间的人,总会拥有充分的时间。
📕 基本概念
在JVM虚拟机种Java对象的内存结构如图所示分为三大块:对象头(Object Header)、实例数据(Instance Data)、对齐填充(Padding)。 对象头:标记字段、类型指针、数组长度(限于数组对象)。
对象头中主要部分相关的数据大小:
-
对象头(Object header)
-
Mark Word:对象的Mark Word部分占4个字节,其内容是一系列的标记位,比如轻量级的标记位(00),偏向锁标记位(01)等等。
-
Class对象指针:Class对象指针的大小也是4个字节,其指向的位置是对象对应的Class对象(其对应的元数据对象)的内存地址。
-
-
对象实际数据:这里面包括了对象的所有成员变量,其大小由各个成员变量的大小决定,比如:byte和boolean是1个字节,short和char是2个字节,int和float是4个字节,long和double是8个字节,refrence是4个字节。
-
对齐填充:最后一部分是对齐填充的字节,按8个字节填充
📕 对象头(Object Header)
📕 Mark Word(标记字段)
用于存储对象自身的运行时数据,如哈希码(HashCode)、GC 分代年龄、锁状态标志、线程持有的锁、偏向线程 ID等。Mark Word被设计成一个非固定的数据结构以便在极小的空间内存储尽量多的信息,它会根据自己的状态复用自己的存储空间。
📕Klass Pointer(类型指针)
对象指向它的类数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例;
📕 Length(长度)
如果对象是一个Java数组,那在对象头中还必须有一块用于记录数组长度的数据。
因为虚拟机可以通过普通 Java 对象的元数据信息确定 Java 对象的大小,但是从数组的元数据中无法确定数组的大小。
在32 位JVM中,存放 Class 指针的空间大小是 4 字节,Mark Word 空间大小也是4字节,因此头部就是 8 字节,如果是数组就需要再加 4 字节表示数组的长度,如下表所示:
在 64 位 JVM 中,开启指针压缩,头部存放 Class 指针的空间大小还是4字节,而 Mark Word 区域会变大,变成 8 字节,也就是头部最少为 12 字节,如下图所示:
📕 实例数据(Instance Data)
- 实例数据中存放了对象的字段信息。无论是从父类继承的,还是在子类中定义的,都保存在实例数据中。按照一定顺序存放,在满足这个顺序的条件下,父类定义的字段又会出现在子类定义的变量之前。
注意:这部分数据的存储顺序会受到虚拟机分配参数(FieldAllocationStyle)和字段在Java源码中定义顺序的影响。
HotSpot虚拟机默认的分配策略如下:
longs/doubles、ints、shorts/chars、bytes/booleans、oop(Ordinary Object Pointers)
-
从分配策略中可以看出,相同宽度的字段总是被分配到一起
-
在满足这个前提的条件下,父类中定义的变量会出现在子类之前
CompactFields = true;
如果 CompactFields参数值为true(默认为true),那子类之中较窄的变量也可能会插入到父类变量的空隙之中。
- 对齐填充:对齐填充不是必然存在的,没有特别的含义,它仅起到占位符的作用。由于 HotSpot VM 的自动内存管理系统要求对象起始地址必须是 8 字节的整数倍,也就是说对象的大小必须是 8 字节的整数倍。对象头部分是 8 字节的倍数,所以当对象实例数据部分没有对齐时,就需要通过对齐填充来补全
📕 实际案例分析
注意:图中的markdown 写错了 应该是 markword ,请见谅!。
在HotspotJVM中,32位机器下,Integer对象的大小是int的几倍?
我们都知道在java语言规范已经规定了int的大小是4个字节,那么Integer对象的大小是多少呢?要知道一个对象的大小,那么必须需要知道对象在虚拟机中的结构是怎样的,来看看Hotsopt中对象在内存中的结构:
在上面这张图里面可以看出,对象在内存中的机构主要包含以下几个部分:
根据上面的图,那么我们可以得出Integer的对象的结构如下:
Integer只有一个int类型的成员变量value,所以其对象实际数据部分的大小是4个字节,然后再在后面填充4个字节达到8字节的对齐,所以可以得出Integer对象的大小是16个字节。
因此,我们可以得出Integer对象的大小是原生的int类型的4倍。
关于对象的内存结构,需要注意数组的内存结构和普通对象的内存结构稍微不同,因为数据有一个长度length字段。所以在对象头后面还多了一个int类型的length字段,占4个字节,接下来才是数组中的数据。如下图:

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Java技术专题-JVM研究系列(26)让你完全攻克内存溢出(OOM)这一难题
每日一句 只有经历地狱般的磨练,才能创造出天堂般的力量。 堆(Heap)内存不足 报错信息: java.lang.OutOfMemoryError: Java heap space 导致原因 代码中可能存在大对象分配 可能存在内存泄露,导致在多次GC之后,还是无法找到一块足够大的内存容纳当前对象。 业务场景会剧增对象数据,应该提升内存空间。 解决方法 检查是否存在大对象的分配,最有可能的是大数组分配 通过jmap命令,把堆内存dump下来,使用mat工具分析一下,检查是否存在内存泄露的问题 如果没有找到明显的内存泄露,使用 -Xms/-Xmx 加大堆内存 还有一点容易被忽略,检查是否有大量的自定义的 Finalizable 对象,也有可能是框架内部提供的,考虑其存在的必要性 方法区溢出 报错信息: java.lang.OutOfMemoryError: PermGen space java.lang.OutOfMemoryError: Metaspace 导致原因 JDK8之前,永久代是HotSot 虚拟机对方法区的具体实现,存放了被虚拟机加载的类信息、常量、静态变量、JIT编译后的代...
- 下一篇
RuoYi-Vue 3.5.0 发布,更多细节优化
若依前后端分离版本v3.5.0 已发布,更新日志: 新增菜单导航显示风格TopNav(false为左侧导航菜单,true为顶部导航菜单) 布局设置支持保存&重置配置 修复树表数据显示不全&加载慢问题 新增IE浏览器版本过低提示页面 用户登录后记录最后登录IP&时间 页面导出按钮点击之后添加遮罩 富文本编辑器支持自定义上传地址 富文本编辑组件新增readOnly属性 页签TagsView新增关闭右侧功能 显隐列组件加载初始默认隐藏列 关闭头像上传窗口还原默认图片 个人信息添加手机&邮箱重复验证 代码生成模板导出按钮点击后添加遮罩 代码生成模板树表操作列添加新增按钮 代码生成模板修复主子表字段重名问题 升级fastjson到最新版1.2.76 升级druid到最新版本v1.2.6 升级mybatis到最新版3.5.6 阻止远程代码执行漏洞 升级oshi到最新版本v5.6.0 velocity剔除commons-collections版本,防止3.2.1版本的反序列化漏洞 数据监控页默认账户密码防止越权访问 修复firefox下表单构建拖拽会新打卡一个选项卡 ...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS8安装Docker,最新的服务器搭配容器使用
- Linux系统CentOS6、CentOS7手动修改IP地址
- 2048小游戏-低调大师作品
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- CentOS8编译安装MySQL8.0.19
- CentOS6,CentOS7官方镜像安装Oracle11G
- CentOS7,8上快速安装Gitea,搭建Git服务器