美文网首页
对象的结构

对象的结构

作者: lsh的学习笔记 | 来源:发表于2020-05-12 22:49 被阅读0次

    对象内存布局(结构)

    对象内存布局(结构)

    对象头

    1.对象头形式

    JVM中对象头的方式有以下两种(以32位JVM为例):

    1.1.普通对象

    |--------------------------------------------------------------|
    |                     Object Header (64 bits)                  |
    |------------------------------------|-------------------------|
    |        Mark Word (32 bits)         |    Klass Word (32 bits) |
    |------------------------------------|-------------------------|
    

    1.2.数组对象

    |---------------------------------------------------------------------------------|
    |                                 Object Header (96 bits)                         |
    |--------------------------------|-----------------------|------------------------|
    |        Mark Word(32bits)       |    Klass Word(32bits) |  array length(32bits)  |
    |--------------------------------|-----------------------|------------------------|
    

    2.对象头的组成

    2.1.Mark Word

    • 存储对象自身运行时数据,如HashCode、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。
    • 长度在32位和64位JVM(未开启压缩指针)中分别为32bit和64bit。
    // 源码注释
    // 32 bits:
    // --------
    //         hash:25 ------------>| age:4    biased_lock:1 lock:2 (normal object)
    //         JavaThread*:23 epoch:2 age:4    biased_lock:1 lock:2 (biased object)
    //         size:32 ------------------------------------------>| (CMS free block)
    //         PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object)
    //
    //  64 bits:
    //  --------
    //  unused:25 hash:31 -->| unused:1   age:4    biased_lock:1 lock:2 (normal object)
    //  JavaThread*:54 epoch:2 unused:1   age:4    biased_lock:1 lock:2 (biased object)
    //  PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object)
    //  size:64 ----------------------------------------------------->| (CMS free block)
    //
    //  unused:25 hash:31 -->| cms_free:1 age:4    biased_lock:1 lock:2 (COOPs && normal object)
    //  JavaThread*:54 epoch:2 cms_free:1 age:4    biased_lock:1 lock:2 (COOPs && biased object)
    //  narrowOop:32 unused:24 cms_free:1 unused:4 promo_bits:3 ----->| (COOPs && CMS promoted object)
    //  unused:21 size:35 -->| cms_free:1 unused:7 ------------------>| (COOPs && CMS free block)
    
    // 伪代码,模拟判断逻辑
    if (lock == 01) {
        if (biased_lock == 0) {
            return "无锁,25bit记录Hash,4bit年龄";
        }
        if (biased_lock == 1) {
            return "偏向锁,23bit记录偏向线程ID,2bit记录偏向时间戳,4bit年龄";
        }
    }
    if (lock == 00) {
        return "轻量锁,30bit记录指向栈中锁记录的指针";
    }
    if (lock == 10) {
        return "重量锁,30bit记录指向管程Monitor的指针";
    }
    if (lock == 11) {
        return "GC标记,不需要记录信息";
    }
    
    1. lock:2位的锁状态标记位,由于希望用尽可能少的二进制位表示尽可能多的信息,所以设置了lock标记。该标记的值不同,整个mark word表示的含义不同。
    2. biased_lock:对象是否启用偏向锁标记,只占1个二进制位。为1时表示对象启用偏向锁,为0时表示对象没有偏向锁。
    3. age:4位的Java对象年龄。在GC中,如果对象在Survivor区复制一次,年龄增加1。当对象达到设定的阈值时,将会晋升到老年代。默认情况下,并行GC的年龄阈值为15,并发GC的年龄阈值为6。由于age只有4位,所以最大值为15,这就是-XX:MaxTenuringThreshold选项最大值为15的原因。
    4. identity_hashcode:25位的对象标识Hash码,采用延迟加载技术。调用方法System.identityHashCode()计算,并会将结果写到该对象头中。当对象被锁定时,该值会移动到管程Monitor中。
    5. thread:持有偏向锁的线程ID。
    6. epoch:偏向时间戳。
    7. ptr_to_lock_record:指向栈中锁记录的指针。
    8. ptr_to_heavyweight_monitor:指向管程Monitor的指针。

    2.2.class pointer

    这一部分用于存储对象的类型指针,该指针指向它的类元数据,JVM通过这个指针确定对象是哪个类的实例。该指针的位长度为JVM的一个字大小,即32位的JVM为32位,64位的JVM为64位。

    如果应用的对象过多,使用64位的指针将浪费大量内存,统计而言,64位的JVM将会比32位的JVM多耗费50%的内存。为了节约内存可以使用选项+UseCompressedOops开启指针压缩,其中,oop即ordinary object pointer普通对象指针。开启(有些版本默认开启)该选项后,下列指针将压缩至32位:

    1. 每个Class的属性指针(即静态字段)
    2. 每个对象的属性指针(即对象字段)
    3. 普通对象数组的每个元素指针

    当然,也不是所有的指针都会压缩,一些特殊类型的指针JVM不会优化,比如指向PermGen的Class对象指针(JDK8中指向元空间的Class对象指针)、本地变量、堆栈元素、入参、返回值和NULL指针等。

    2.3.array length

    如果对象是一个数组,那么对象头还需要有额外的空间用于存储数组的长度,这部分数据的长度也随着JVM架构的不同而不同:32位的JVM上,长度为32位;64位JVM则为64位。64位JVM如果开启+UseCompressedOops选项,该区域长度也将由64位压缩至32位。

    实例数据(Instance Data)

    • 即Java代码中定义的字段内容包含从父类继承下来的
    • 存储顺序:受到虚拟机分配策略参数(FieldsAllocationStyle)和字段在Java源码中定义顺序的影响。HotSpot虚拟机默认的分配策略为longs/doubles、ints、shorts/chars、bytes/booleans、oops(OrdinaryObjectPointers),从分配策略中可以看出,相同宽度的字段总是被分配到一起。在满足这个前提条件的情况下,在父类中定义的变量会出现在子类之前。如果CompactFields参数值为true(默认为true),那么子类之中较窄的变量也可能会插入到父类变量的空隙之中。

    对齐填充

    • 不是必然存在,也没有特别含义,只起占位符作用。
    • HotSpotVM的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说,就是对象的大小必须是8字节整数倍

    对象访问定位

    对象访问定位

    参考文献:

    Java对象头详解
    markOop.hpp
    CompressedOops

    相关文章

      网友评论

          本文标题:对象的结构

          本文链接:https://www.haomeiwen.com/subject/zsjwnhtx.html