java对象内存布局介绍

本文介绍了Java对象的组成,包括对象头(Object Header)、实例数据(instance data)和对齐填充。对象头由Mark Word和Class Pointer组成,存储对象的标识、GC信息和锁状态。实例数据可能因字段重排列进行内存对齐。使用JOL工具验证了这些结论,并强调对象大小必须满足8字节对齐。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对象的组成

根据java虚拟机规范里面的描述:java对象分为三部分:对象头(Object Header), 实例数据(instance data),对齐填充(padding)。如图:
在这里插入图片描述
数组与对象类似,只是对象头部分多了数组长度Length的存储长度为4字节。

对象头(Object Header):

从图片上得知对象头分为两部分:Mark Word 与 Class Pointer(类型指针)。

Mark Word存储了对象的hashCode、GC信息、锁信息三部分,Class Pointer存储了指向“类对象信息的指针”。在32位JVM上对象头占用的大小是8字节,64位JVM则是16字节,两种类型的Mark Word 和 Class Pointer各占一半空间大小。
在64位JVM上有一个压缩指针选项-XX:+UseCompressedOops,默认是开启的。开启之后Class Pointer部分就会压缩为4字节,此时对象头大小就会缩小到12字节。
下面是用一张图展示 32位JVM上对象头的内存分布,方便理解。
在这里插入图片描述

实例数据(instance data):

有一种错觉就是实例数据里面通常只存了数据本身,但实际上这部分JVM也做了对齐填充,这样做的目的是为了内存对齐,为了实现这个效果JVM会执行一个字段重排列的操作。

字段重排列: 故名思义就是JVM在分配内存时不一定是完全按照类定义的字段顺序去分配,而是根据JVM选项-XX:FieldsAllocationStyle(默认是1)来进行排序。对于所有的

-XX:FieldsAllocationStyle配置选项来说都会遵守以下两个原则:

1、如果一个字段的大小为S字节,则对象开始的位置到该字段的位置的偏移量一定满足:

该字段的位置 - 对象开始位置 = mS (m >=1) 即是S的m倍。

在64位JVM开启压缩指针的基础上举个例子:

对象的分配第一个字段是个long 类型,这时对 象头的大小为12个字节,long的大小8个字节。14不能整除8,这时JVM会填充4个字节。让字段的位置从16开始,这样压缩节省出来的4字节空间又还回去了。。。。

2、子类继承父类字段的偏移量一定和父类是一致的。在具体的64位JVM实现中对还会对比子类的实例数据进行以下对齐:如果开启了压缩指针则子类第一个字段的偏移量是4N,关闭压缩指针之后是8N。

JOL 工具来验证上面的结论

1.ms验证

import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.vm.VM;

/**
 * java 对象内存分布
 */

class A{
    long i;
}

public class MemoryLayoutTest {

    public static void main(String[] args){

        System.out.println(VM.current().details());
        System.out.println(ClassLayout.parseClass(A.class).toPrintable());
    }

}

得到的结果

memory.A object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0    12        (object header)                           N/A
     12     4        (alignment/padding gap)                  
     16     8   long A.i                                       N/A
Instance size: 24 bytes
Space losses: 4 bytes internal + 0 bytes external = 4 bytes total

为了满足mS,进行了4个字节的填充(16 - 0 = 2*8)
2.偏移量是一致

class A{
    long i;
}

class B extends  A{
    int  j;
}

public class MemoryLayoutTest {

    public static void main(String[] args){

        System.out.println(VM.current().details());
        System.out.println(ClassLayout.parseClass(B.class).toPrintable());
    }

}
memory.B object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0    12        (object header)                           N/A
     12     4        (alignment/padding gap)                  
     16     8   long A.i                                       N/A
     24     4    int B.j                                       N/A
     28     4        (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 4 bytes internal + 4 bytes external = 8 bytes total

对齐填充:

这里的对齐填充专指对象末尾的填充,如果对象填充完实例数据后的大小不满足"8N",则填充到8N,其实在上面运行结果中就已经有提示了。
上面大小到28时满足不了8N,故填充了4字节,使得实例数据大小达到32字节,满足了8N。
在这里插入图片描述

总结:

java对象由三部分组成:对象头,实例数据,对齐填充。

对象头包括Mark Word和 Class Pointer。Mark Word包括对象hashCode,GC信息和锁信息。在32位JVM下对象头大小为8字节。64位16字节,开启压缩指针后为12字节,压缩的是Class Pointer。

实例数据也会有对齐填充。(实例数据填充)

对象大小一定满足8N。(对象填充)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值