二、架构师-高并发与多线程-java对象内存布局

首先以一个问题引出这次的内容:Object o = new Obeject()在内存中占了多少个字节?
要回答这个问题就要首先清楚对象在内存中的内存布局。

对象内存布局

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

测试代码:

工具:JOL = java Object Layout

引入jar包:

<!-- https://mvnrepository.com/artifact/org.openjdk.jol/jol-core -->
        <dependency>
            <groupId>org.openjdk.jol</groupId>
            <artifactId>jol-core</artifactId>
            <version>0.14</version>
        </dependency>
public class T01 {
    public static void main(String[] args) {

        Object o = new Object();
        ClassLayout classLayout = ClassLayout.parseInstance(o);
        System.out.println(classLayout.toPrintable());

		Long l = new Long(1);
        ClassLayout classLayout1 = ClassLayout.parseInstance(l);
        System.out.println(classLayout1.toPrintable());

		ClassLayout classLayout2 = ClassLayout.parseInstance(new HashMap<>());
        System.out.println(classLayout2.toPrintable());
    }
}

运行结果:

java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

java.lang.Long object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           f5 22 00 f8 (11110101 00100010 00000000 11111000) (-134208779)
     12     4        (alignment/padding gap)                  
     16     8   long Long.value                                1
Instance size: 24 bytes
Space losses: 4 bytes internal + 0 bytes external = 4 bytes total

java.util.HashMap object internals:
 OFFSET  SIZE                       TYPE DESCRIPTION                               VALUE
      0     4                            (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4                            (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4                            (object header)                           a8 37 00 f8 (10101000 00110111 00000000 11111000) (-134203480)
     12     4              java.util.Set AbstractMap.keySet                        null
     16     4       java.util.Collection AbstractMap.values                        null
     20     4                        int HashMap.size                              0
     24     4                        int HashMap.modCount                          0
     28     4                        int HashMap.threshold                         0
     32     4                      float HashMap.loadFactor                        0.75
     36     4   java.util.HashMap.Node[] HashMap.table                             null
     40     4              java.util.Set HashMap.entrySet                          null
     44     4                            (loss due to the next object alignment)
Instance size: 48 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

以Object为例,分析每个组成部分。

对象头

从图片上得知对象头分为两部分:Mark Word 与 Class Pointer(类型指针,可以理解是对象的引用)。

Mark Word存储了对象的hashCode、GC信息、锁信息三部分Class Pointer存储了指向类对象信息的指针。在32位JVM上对象头占用的大小是8字节,64位JVM则是16字节,两种类型的Mark Word 和 Class Pointer各占一半空间大小下一篇会详细分析Mark word中的锁信息。
在这里插入图片描述

注意:在64位JVM上有一个压缩指针选项**-XX:+UseCompressedOops**,默认是开启的。开启之后Class Pointer部分就会压缩为4字节,此时对象头大小就会缩小到12字节。

计算机位数是否开启压缩指针mark wordclass pointer
64位84
64位88
32位\44

instance data 实例数据

这部分不仅存了成员变量的数据, 而且也有为了内存对齐的填充数据,即:instance data = 成员变量值 + padding数据。
为了实现这个内存对齐的效果,jvm不仅会padding而且还执行一个字段重排序的操作。

字段重排序:jvm再分配内存时不是完全按照类定义的字段顺序去分配,而是根据jvm选项:-XX:FieldsAllocationStyle(默认是1)来进行排序。对于所有的-XX:FieldsAllocationStyle配置选项来说都会遵守以下两个原则:

  1. 如果一个字段的大小为S字节,则对象的开始位置到该字段的偏移量一定满足:该字段的位置 - 对象开始位置 = nS(n >= 1)为整数,即S的n倍。在64位jvm开启压缩指针的基础上举个例子:

对象的分配第一个字段是long数据,这时对象头的大小为12个字节,long的大小8个字节。12不能整除8,这时jvm会填充4个字节,让字段从16开始。如下图:在这里插入图片描述

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

a.验证子类继承父类字段的偏移量一定和父类一致的:在这里插入图片描述
看到子类B中字段i的偏移量上面父类A中i的偏移量是一致的,都在16位置。而且子类B的第一个字段j的偏移量正好满足"4N"。
b.验证关闭压缩指针-XX:-UseCompressedOops之后,子类的第一个字段偏移量是8N:在这里插入图片描述
如图,子类B的第一个字段偏移量是被填充到24,红色箭头所指的填充块;关闭压缩指针之后子类的第一个字段偏移量是8N。

padding 填充区

padding填充区和instance data区的填充区是有区别的。padding填充区是类级别的填充,专指对齐填充对象末尾的,所有(loss due to the next object alignment)标识的地方,下图红色区域;而instance data区的填充区是全局变量级别的,是为了对齐全局变量,下图蓝色区域。

填充规则:如果对象填充完实例数据后的大小不满足"8N",则填充到8N

在这里插入图片描述

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

对象头包括Mark Word和 Class Pointer。Mark Word包括对象hashCode,GC信息和锁信息。在32位JVM下对象头大小为8字节。64位16字节,开启压缩指针后为12字节,压缩的是Class Pointer,压缩为4。
实例数据也会有对齐填充。
对象大小一定满足8N。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值