java对象的内存布局

我们用如下代码来说一下java对象的内存布局。

class A {

    long l;

    int i;

}

class B extends A {

    long l;

    int i;

}

 

如图所示展示的是new B()在堆中的内存模型:

java对象的内存布局_第1张图片

        JVM中,每个对象都有一个对象头(图中黄色部分),由标记字段和类型指针构成,标记字段存储hashCode, GC信息,锁信息,类型指针指向该对象的类。

        64位JVM中,标记字段和类型指针各占64位(一共16个字节),比如Integer类,仅有一个私有的int字段(4个字节),而头部额外多出16个字节,因此,每一个Integer的内存额外开销至少是400%,这也是java要引入基本类型的原因之一。

        为了减少对象内存的使用,64位JVM引入了压缩指针的概念(虚拟机选项-XX:+UseCompressedOops,默认开启),将堆中的64位指针压缩成32位,这样以来,对象头占用的内存就从16字节下降到了12字节。

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

        内存对齐不仅存在于对象与对象之间,也存在于对象的字段之间。

        字段内存对齐的其中一个原因,是让字段只出现在同一 CPU 的缓存行中,来提升效率。

        有内存对齐就会有内存浪费的情况,所以为了减少内存浪费,并达到内存对齐的目的,JVM就会将字段重新排列。

        字段重排列遵循两个规则:

1、如果一个字段占c个字节,那么它的偏移量是c的倍数。比如long占8个字节,那么它的偏移必须是8的倍数。

2、jvm回对齐子类字段的起始位置,对于开启压缩指针的64位jvm来说,子类的第一个字段要对齐到4的倍数,对于没有开启的来说,要对齐到8的倍数。

        我们还是以上面的例子来说,比如当开启了指针压缩之后,头占用12个字节,这个时候有一个A.l需要填充,由于long型占用8个字节,所以它的偏移量只能是8的倍数(规定),所以它的偏移量只能是16,这样中间就浪费了4个字节,可以通过字段重排列节省浪费的空间。

        如图所示:

java对象的内存布局_第2张图片

        可以看到通过字段重排列,节省了空间。

你可能感兴趣的:(java)