10 | Java对象的内存布局
该思维导图由 AI 生成,仅供参考
- 深入了解
- 翻译
- 解释
- 总结
Java对象的内存布局对于Java程序员来说至关重要。本文介绍了在Java程序中新建对象的多种方式,以及对象的内存布局中的压缩指针和字段重排列。在64位Java虚拟机中,对象头的大小为16个字节,而压缩指针的引入可以将对象头的大小降至12个字节,从而减少对象的内存占用。压缩指针的原理类似于将停车位号转换为车号,通过内存对齐来实现。另外,字段重排列是为了达到内存对齐的目的,子类继承字段的偏移量需要与父类对应字段的偏移量保持一致。这些概念对于理解Java对象的内存布局和优化内存使用具有重要意义。 文章还介绍了启用和关闭压缩指针时,对象字段的分布情况,以及对虚共享问题的解决方案。压缩指针的启用可以减少对象内存占用,而关闭压缩指针时,对象字段的起始位置需对齐至8N,导致字段前后各有4字节的空白。此外,文章还提到了新的注释@Contended的引入,用于解决对象字段之间的虚共享问题,但也会影响字段的排列。 总的来说,本文详细介绍了Java虚拟机构造对象的方式、对象的大小和内存布局,以及压缩指针和字段重排列的相关概念。读者可以通过JOL工具来打印工程中类的字段分布情况,从而更好地理解和优化内存使用。 综上所述,本文内容涵盖了Java对象内存布局的重要概念和优化方法,对于Java程序员来说具有很高的参考价值。
《深入拆解 Java 虚拟机》,新⼈⾸单¥59
全部留言(56)
- 最新
- 精选
- life is short, enjoy mor...对象头 每个对象都有一个对象头,对象头包括两部分,标记信息和类型指针。 标记信息包括哈希值,锁信息,GC信息。类型指针指向这个对象的class。 两个信息分别占用8个字节,所以每个对象的额外内存为16个字节。很消耗内存。 压缩指针 为了减少类型指针的内存占用,将64位指针压缩至32位,进而节约内存。之前64位寻址,寻的是字节。现在32位寻址,寻的是变量。再加上内存对齐(补齐为8的倍数),可以每次寻变量都以一定的规则寻找,并且一定可以找得到。 内存对齐 内存对齐的另一个好处是,使得CPU缓存行可以更好的实施。保证每个变量都只出现在一条缓存行中,不会出现跨行缓存。提高程序的执行效率。 字段重排序 其实就是更好的执行内存对齐标准,会调整字段在内存中的分布,达到方便寻址和节省空间的目的。 虚共享 当两个线程分别访问一个对象中的不同volatile字段,理论上是不涉及变量共享和同步要求的。但是如果两个volatile字段处于同一个CPU缓存行中,对其中一个volatile字段的写操作,会导致整个缓存行的写回和读取操作,进而影响到了另一个volatile变量,也就是实际上的共享问题。 @Contented注解 该注解就是用来解决虚共享问题的,被该注解标识的变量,会独占一个CPU缓存行。但也因此浪费了大量的内存空间。
作者回复: 赞总结!
2018-10-11279 - 神佑小鹿在默认情况下,Java 虚拟机中的 32 位压缩指针可以寻址到 2 的 35 次方个字节,也就是 32GB 的地址空间(超过 32GB 则会关闭压缩指针)。 这里为啥是 35 ????
作者回复: 以8字节为单位,跟ObjectAlignmentInBytes有关
2019-11-2738 - 一个坏人老师好,请教一下:“自动内存管理系统为什么要求对象的大小必须是8字节的整数倍?”,即内存对齐的根本原因在于?
作者回复: 在某些体系架构上,不对齐的话内存读写会报错。 在X86_64上,一个是为了让字段也能对齐,这样就不会出现字段横跨两个缓存行的情况,另一个原因更像个副作用,就是对象地址最后三位一直是0,JVM利用这个特性来实现压缩指针,也可以用这三位来记录一些额外信息
2018-08-2537 - 大能猫最近研究String时遇到一个跟Java内存相关的问题:常量池里到底有没有存放对象? 常量池主要存放两大类常量:字面量(Literal)和符号引用(Symbolic Reference); 如果常量池里有一个“hello”的字面量,这个字面量算是一个对象吗?如果不算对象,那么它所指向的对象又存放在哪里呢
作者回复: String literal指向的对象存放在JVM的String pool里。
2018-08-146 - everyok22你文章里说: 64位的JVM中,不采用压缩指针的方式,标记字段与类型指针分别占用8个字节,而采用了压缩指针标记字段与类型指针都会压成32位(8字节)那对象头不是只占用8个字节么,为什么你说是12个字节
作者回复: 标记字段没有被压缩。
2018-08-224 - 周仕林对象头的组成如果阅读过周志明的JVM虚拟机会发现作者说的有一些有失偏颇,对象头的组成是对象运行信息,类型指针(如果对象访问采用直接指针),数组长度(如果对象是数组)
作者回复: 周老师书上的是提炼之后的抽象说法。 HotSpot的对象头一直是mark word(标记字段)和类型指针。如果对源代码有兴趣可以查看OpenJDK源代码目录下,src/hotspot/share/oops/oop.hpp里的class oopDesc。数组对象头是同目录下的arrayOop.hpp里的class arrayOopDesc
2018-08-213 - 贾智文默认情况下32位可以寻址2的35次,应该是因为地址是32位乘以2的三次(默认对其为8),那么如果不采用压缩指针,能够寻址的范围应该是2的64次对吧。然后之前模糊的地方就是觉得两个寻址范围并不一致,是不是可以这么理解并没有通过压缩指针让两个寻址范围一致,而是通过压缩指针放大了32位的寻址空间使它够用了
作者回复: 对的
2018-08-152 - 熊猫酒仙接触过C/C++的内存字节对齐,就比较好理解本章内容了。希望老师后面讲讲java内存在并发上的相关机制,譬如搞不懂的内存障是怎么实现的!
作者回复: 底层的内存屏障,比如说mfence,lock指令,是用来防止指令重排布的。Java里的内存屏障除了生成上述底层指令外,还会限制即时编译器对内存访问的重排序。之后我会讲Java内存模型。
2018-08-142 - 贾智文有一点想不明白,既然内存对齐是八位而不是举例的两位为什么空间只是从64位变成32而不是从64变成8
作者回复: 不是很理解你的问题。 对象间需要内存对齐至8字节。64位和32位对应8字节和4字节。
2018-08-1521 - 槑·先生在极客时间买了不少课程了,这个系列算是难读的。2019-06-152569