对象头
Java虚拟机中每个Java对象都有一个对象头,对象头由标记字段和类型指针构成。其中标记字段用以存储Java虚拟机有关对象的运行数据,如哈希码、GC信息及锁信息,而指针类型指向该对象的类。
压缩指针
在64位的虚拟机中,对象头的标记字段占64位,而类型指针又占64位。也就是说一个对象额外占用的字节就是16个字节。以Integer对象为例,它仅有一个int类型的私有字段,占4个字节。因此,每个Integer的额外开销至少400%,这也就是Java为什么要引入基本数据类型的原因之一。为了减少内存开销,64位Java虚拟机引入了压缩指针概念(对应虚拟机选项 -XX:+UseCompressedOops,默认开启),将堆中原本64位的Java对象指针压缩成32位的。
这样一来,对象头的类型指针也会被压缩成32位,使得对象头大小从16字节降低为12字节。压缩指针不仅可以作用对象头的类型指针,还可以作用引用类型的字段,引用类型的数组。
压缩指针原理
默认情况下,Java虚拟机中对象的起始地址需要对齐至8的倍数(这个概念我们称之为内存对齐(对应虚拟机选项 -XX:ObjectAlignmentInBytes,默认值为 8)。如果一个对象用不到8N字节,那么空白的那部分空间就白白浪费掉了。这些浪费掉的空间我们称之为对象之间的填充。默认情况下,Java虚拟机中32位的指针可以寻址到2的35次方,也就是32GB的内存空间(超过32位会关闭压缩指针)。在对压缩指针解引用时,我们需要将其左移3位,再加上一个固定的偏移量,便可以寻址到32GB地址空间伪64位指针了。
此外,我们可以配置刚刚提到的内存对齐选项(-XX:ObjectAlignmentInBytes)来进一步提升内存寻址范围。但是,这也可能增加对象填充,导致压缩指针没有打到节省空间效果。
关闭压缩指针
就算关闭了压缩指针,Java虚拟机也会进行内存对齐。内存对齐不仅在于对象和对象之间,也存在于对象的各个字段之间。比如说,Java虚拟机中的long字段、double字段,以及非压缩指针状态下的引用字段为8的倍数。
内存对齐原因
内存对齐的一个原因是让字段出现在同一CPU的缓存中。如果字段不对齐,那么就有可能出现跨缓存行的字段。也就是说,该字段的读取的读取可能需要跨两个缓存行,而改字段的存储也可能同时污染两个缓存行。这种情况对程序的执行效率是不利的。
本文整理自
仅做个人学习总结所用,遵循CC 4.0 BY-SA版权协议,如有侵权请联系删除!