网站首页 > 博客文章 正文
Java对象的内存布局
1 前言
新建对象的方式:
- Object.clone,反序列化直接复制已有数据,初始化新建对象的实例字段
- Unsafe.allocateInstance没有初始化实例字段
- new
- 反射new和反射机制,都是通过调用构造器来初始化实例字段
如new编译而成的字节码包含:
- 请求内存的new指令
- 调用构造器的invokespecial指令
若一个类未定义任何构造器, 则Java编译器会自动添加一个无参构造器:
但子类构造器需调用父类构造器:
- 若父类存在无参构造器,该调用可以是隐式,即Java编译器会自动添加对父类构造器的调用
- 若父类没有无参构造器,则子类构造器需显式调用父类带参构造器
显式调用又可分为:
- super关键字调用父类构造器
- this关键字调用同一类中的其它构造器
都要作为构造器的第一条语句,以优先初始化继承得的父类字段。(但这也能通过调用其他生成参数的方法或字节码注入来绕开)
调用一个构造器时,优先调用父类构造器,直至Object类。这些构造器的调用者皆为同一对象,即通过new指令新建而来的对象。通过new指令新建的对象,其内存含所有父类实例字段。虽子类无法访问父类private实例字段或子类的实例字段隐藏了父类的同名实例字段,但子类实例还是会为这些父类实例字段分配内存。
那这些字段在内存如何分布的呢?
2 压缩指针
JVM,每个Java对象都有对象头(object header),由标记字段和类型指针构成:
- 标记字段,存储JVM有关该对象的运行数据,如哈希码、GC信息及锁信息
- 类型指针,指向该对象的类
64位JVM,对象头的标记字段占64位,类型指针又占64位。每个Java对象在内存中额外开销就是16字节。Integer类仅有一个int类型私有字段,占4字节。因此,每个Integer对象额外内存开销至少400%。这也是引入基本类型的原因之一。
为降低对象内存使用量,64位JVM引入压缩指针[1](对应虚拟机选项-XX:+UseCompressedOops,默认开启),将堆中原本64位的Java对象指针压缩成32位。
这样,对象头中的类型指针也会被压缩成32位,使对象头大小从16字节降至12字节。
2.1 作用范围
- 对象头的类型指针
- 引用类型的字段
- 引用类型数组
2.2 原理
路上全是房车,而且每辆房车恰好占据两个停车位。按序编号,停在0号和1号停车位上的叫0号车,停在2号和3号停车位上的叫1号车,依次类推。
原本内存寻址用车位号。如我有一个值为6的指针,代表第6车位,沿这指针可找到3号车。
规定指针里存的值是车号,如3指代3号车。当查找3号,可将该指针值乘2,再沿着6号车位找到3号车。
这样32位压缩指针最多可标记2的32次方辆车,对应着2的33次方个车位。房车也有大小之分。大房车占据车位可能是三个甚至是更多。不过这并不会影响寻址算法:只需跳过部分车号,便可保持原本车号*2的寻址系统。
上述模型有一个前提,就是每辆车都从偶数号车位停起。这称为内存对齐(对应虚拟机选项
-XX:ObjectAlignmentInBytes,默认值为8)。
默认Java虚拟机堆中对象起始地址需对齐至8倍数。如一个对象用不到8N字节,那空白部分空间就浪费了。这些浪费的空间称为对象间的填充(padding)。
默认Java虚拟机中32位压缩指针可寻址到2的35次方个字节,即32GB地址空间(超过32GB则会关闭压缩指针)。
在对压缩指针解引用时,需将其左移3位,再加上一个固定偏移量,便可得到能寻址32GB地址空间的伪64位指针。
可通过配置刚提到的内存对齐选项(
-XX:ObjectAlignmentInBytes)进步提升寻址范围。但是,这同时也可能增加对象间填充,导致压缩指针没有达到原本节省空间的效果。
举例来说,如规定每辆车都需从偶数车位号停起,那占据两个车位的小房车刚好,而对需三个车位的大房车仅是浪费一个车位。
但如规定需从4倍数号车位停起,那小房车则会浪费两车位,而大房车至多可能浪费三车位。
就算关闭压缩指针,Java虚拟机还是会进行内存对齐。内存对齐不仅存在于对象与对象间,也存在对象中的字段间。比如说,Java虚拟机要求long字段、double字段及非压缩指针状态下的引用字段地址为8的倍数。
字段内存对齐的其中一个原因是让字段只出现在同一CPU的缓存行。如字段不对齐,可能出现跨缓存行的字段,即该字段的读取可能需替换两个缓存行,而该字段的存储也会同时污染两个缓存行。这对程序执行效率都不好。
3 字段重排列
JVM重新分配字段先后顺序,以达到内存对齐。Java虚拟机中有三种排列方法(对应Java虚拟机选项-XX:FieldsAllocationStyle,默认值为1),但都遵循如下规则:
- 如一个字段占C个字节,那该字段偏移量需对齐至NC偏移量指字段地址与对象的起始地址差值。long类仅有一个long类型实例字段。在使用压缩指针的64位虚拟机,尽管对象头12字节,该long类型字段的偏移量也只能是16,而中间空着的4字节便会被浪费掉。
- 子类所继承字段的偏移量,要与父类对应字段的偏移量保持一致
JVM还会对齐子类字段的起始位置:
- 使用压缩指针的64位虚拟机,子类第一个字段需对齐至4N
- 关闭压缩指针的64位虚拟机,子类第一个字段需对齐至8N
两个类A和B,其中B继承A。A和B各自定义一个long类型的实例字段和一个int类型实例字段。
B类在启用压缩指针和未启用压缩指针时,各字段偏移量:
启用压缩指针,JVM将A类int字段置于long字段前,以填充因long字段对齐造成的4字节缺口。由于对象整体大小需对齐至8N,因此对象最后会有4字节空白填充。
当关闭压缩指针时,B类字段起始位置需对齐至8N。那B类字段前后各有4字节空白。
Java 8还引入新注释@Contended,解决对象字段之间的虚共享(false sharing)问题[2]。也影响字段排列。
4 虚共享
假设两线程分别访问同一对象中不同的volatile字段,逻辑上无共享内容,因此无需同步。但若这俩字段恰在同一缓存行,则对这些字段写操作会导致缓存行的写回,即造成了实质上的共享。
Java虚拟机会让不同@Contended字段处于独立缓存行,因此大量空间被浪费。可查阅Contended字段的内存布局。注意使用虚拟机选项-XX:-RestrictContended。
5 总结
本文介绍JVM构造对象的方式,所构造对象的大小,以及对象的内存布局。
new语句会被编译为new指令及对构造器调用。每个类的构造器都会直接或者间接调用父类构造器,且在同一实例中初始化相应字段。
JVM引入压缩指针,将原本64位指针压缩成32位。压缩指针要求JVM堆中对象的起始地址要对齐至8倍数,还会对每个类的字段进行重排列,使得字段也内存对齐。
使用JOL工具打印你工程中的类的字段分布:
curl -L -O http://central.maven.org/maven2/org/openjdk/jol/jol-cli/0.9/jol-cli-0.9-full.jar
java -cp jol-cli-0.9-full.jar org.openjdk.jol.Main internals java.lang.String
[1] https://wiki.openjdk.java.net/display/HotSpot/CompressedOops
[2] http://openjdk.java.net/jeps/142
猜你喜欢
- 2025-05-14 JAVA程序员自救之路——Elasticsearch向量搜索
- 2025-05-14 探秘Java程序的“内存大爆炸”:JVM内存溢出问题排查
- 2025-05-14 Java 探秘:如何找出数组中重复的数字
- 2025-05-14 线上问题解决:java内存溢出问题分析,定位及解决
- 2025-05-14 Java虚拟机内存管理深度解读
- 2025-05-14 Java程序内存泄漏问题优化全攻略
- 2025-05-14 Jprofile解析dump文件使用详解
- 2025-05-14 Java中常见的内存泄漏场景解析
- 2025-05-14 Java内存泄漏暗杀指南!3招揪出8G“内存刺客”(附排查神器)
- 2025-05-14 Java内存分析工具——jmap
你 发表评论:
欢迎- 07-07Xiaomi Enters SUV Market with YU7 Launch, Targeting Tesla with Bold Pricing and High-Tech Features
- 07-07Black Sesame Maps Expansion Into Robotics With New Edge AI Strategy
- 07-07Wuhan's 'Black Tech' Powers China's Cross-Border Push with Niche Electronics and Scientific Firepower
- 07-07Maven 干货 全篇共:28232 字。预计阅读时间:110 分钟。建议收藏!
- 07-07IT运维必会的30个工具(it运维工具软件)
- 07-07开源项目有你需要的吗?(开源项目什么意思)
- 07-07自动化测试早就跑起来了,为什么测试管理还像在走路?
- 07-07Cursor 最强竞争对手来了,专治复杂大项目,免费一个月
- 最近发表
-
- Xiaomi Enters SUV Market with YU7 Launch, Targeting Tesla with Bold Pricing and High-Tech Features
- Black Sesame Maps Expansion Into Robotics With New Edge AI Strategy
- Wuhan's 'Black Tech' Powers China's Cross-Border Push with Niche Electronics and Scientific Firepower
- Maven 干货 全篇共:28232 字。预计阅读时间:110 分钟。建议收藏!
- IT运维必会的30个工具(it运维工具软件)
- 开源项目有你需要的吗?(开源项目什么意思)
- 自动化测试早就跑起来了,为什么测试管理还像在走路?
- Cursor 最强竞争对手来了,专治复杂大项目,免费一个月
- Cursor 太贵?这套「Cline+OpenRouter+Deepseek+Trae」组合拳更香
- 为什么没人真的用好RAG,坑都在哪里? 谈谈RAG技术架构的演进方向
- 标签列表
-
- ifneq (61)
- 字符串长度在线 (61)
- messagesource (56)
- aspose.pdf破解版 (56)
- promise.race (63)
- 2019cad序列号和密钥激活码 (62)
- window.performance (66)
- qt删除文件夹 (72)
- mysqlcaching_sha2_password (64)
- ubuntu升级gcc (58)
- nacos启动失败 (64)
- ssh-add (70)
- jwt漏洞 (58)
- macos14下载 (58)
- yarnnode (62)
- abstractqueuedsynchronizer (64)
- source~/.bashrc没有那个文件或目录 (65)
- springboot整合activiti工作流 (70)
- jmeter插件下载 (61)
- 抓包分析 (60)
- idea创建mavenweb项目 (65)
- vue回到顶部 (57)
- qcombobox样式表 (68)
- tomcatundertow (58)
- pastemac (61)
本文暂时没有评论,来添加一个吧(●'◡'●)