Java虚拟机学习10 | Java对象的内存布局

https://time.geekbang.org/column/article/13081

对象

Java创建对象的方式有以下:

  • new语句:通过调用构造器初始化,它编译成的字节码包含用来创建内存的new指令,以及用来初始化的invokespecial
  • 反射:通过调用构造器初始化
  • Object.clone
  • 反序列化
  • Unsafe.allocateInstance

压缩指针

为什么需要压缩指针

 在Java虚拟机中,每一个对象都有一个对象头(Object header),它包含标记字段和类型指针;其中标记字段存储Java虚拟机有关该对象的运行数据,比如哈希码,GC信息和锁信息;类型指针指向该对象的类.

在64位Java虚拟机中,对象头的标记字段占64位(8字节),类型指针占64位(8字节);所以每个Java对象在内存中的额外开销是16字节;以Integer为例,它只有一个私有int类型,占4字节,但额外开销是有效的4倍

因此为了减少内存消耗,提出压缩指针的概念,将堆中原本64位的对象指针压缩为32位,这样对象头的类型指针也将压缩为32位,这样额外开销就降为12字节

压缩指针的原理

原本的内存寻址原理是在压缩的时候,将地址右移3位(除以8),再加上相应的偏移量存到内存中;解引用的时候就是反着来.

内存对齐

内存对齐(对应虚拟机选项-XX:ObjectAlignmentInBytes,默认值为8):

默认情况下,堆中对象的起始地址需要对齐至8 的倍数,如果一个对象用不到8N 个字节,那么空白的那部分空间就浪费掉了. 这些浪费掉的空间我们称之为对象间的填充.

在默认情况下,虚拟机中的32 位压缩指针可以寻址到2 的35 (32+3)次方个字节,也就是32GB 的地址空间(超过32GB 则会关闭压缩指针).

此外,我们可以通过配置刚刚提到的内存对齐选项(-XX:ObjectAlignmentInBytes)来进一步提升寻址范围.但是,这同时也可能增加对象间填充,导致压缩指针没有达到原本节省空间的效果.

内存对齐不仅存在于对象并且存在于对象中的字段.比如说,Java 虚拟机要求long 字段 double 字段,以及非压缩指针状态下的引用字段地址为8 的倍数. 原因就是让字段只出现在同一CPU 的缓存行中. 如果字段不是对齐的,那么就有可能出现跨缓存行的字段. 也就是说,该字段的读取 存储会涉及两个缓存行,对程序的执行效率而言都是不利的.

扫描二维码关注公众号,回复: 6081184 查看本文章

就算是关闭了压缩指针,Java 虚拟机还是会进行内存对齐

字段重排列

字段重排列:顾名思义,就是Java虚拟机重新排列字段的先后顺序,以达到内存对齐的目的.

Java虚拟机有三中排列方式,都会遵循以下两个规则:

  • 如果一个字段占据 C 个字节,那么该字段的偏移量需要对齐至 NC. 这里偏移量指的是字段地址与对象的起始地址差值.
  • 子类所继承字段的偏移量,需要与父类对应字段的偏移量保持一致.

猜你喜欢

转载自blog.csdn.net/qq_34332035/article/details/88029964