对象的内存布局解析

概念说明

  Hotspot虚拟机中,对象在内存中存储的布局可以分为三块区域:对象头(Header)、实例数据 (Instance Data)和对齐填充(Padding)

    • 对象头:比如 hash码,对象所属的年代,对象锁,锁状态标志,偏向锁(线程)ID,偏向时间,数组长度(数组对象才有)等。
    • 实例数据:存放类的属性数据信息,包括父类的属性信息;
    • 对齐填充:由于虚拟机要求 对象起始地址必须是8字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐。

  内存布局图示:


                  

 

对象头详解

  HotSpot虚拟机的对象头包括:

    1.Mark Word

      用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄锁状态标志线程持有的锁偏向线程ID偏向时间戳等。

      这部分数据的长度在32位和64位的虚拟机中分别为32bit和64bit,官方称它为“Mark Word”。(要知道1字节为8bit,故非8字节即4字节。)

    2.Klass Pointer(它的类元数据的指针)

      对象头的另外一部分是klass类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。

      32位4字节,64位开启指针压缩或最大堆内存<32g时4字节,否则8字节。(故非8字节即4字节。)

      jdk1.8默认开启指针压缩后为4字节,当在JVM参数中关闭指针压缩(-XX:-UseCompressedOops)后,长度为8字节。

    3.数组长度(只有数组对象有)

      如果对象是一个数组, 那在对象头中还必须有一块数据用于记录数组长度。 4字节。非数组对象则是0。

 

使用JOL工具查看内存布局(验证理论步骤)

  JOL工具说明

    查看普通java对象的内部布局工具JOL(JAVA OBJECT LAYOUT),使用此工具可以查看new出来的一个java对象的内部布局,以及一个普通的java对象占用多少字节。

    引入maven依赖(在pom.xml文件处添加)

<!-- 查看Java 对象布局、大小工具 -->
<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.9</version>
</dependency>

 

  使用方法

//查看对象内部信息
System.out.println(ClassLayout.parseInstance(obj).toPrintable());

  测试

    案例代码

public class ObjectJolTest {
    public static void main(String[] args) {
        Object obj = new TestObject1();
        System.out.println(ClassLayout.parseInstance(obj).toPrintable());
        Object obj2 = new TestObject2();
        System.out.println(ClassLayout.parseInstance(obj2).toPrintable());
    }
}

class TestObject1{}
class TestObject2{
    private boolean flag;
    private long  p;
}

 

    案例分析

      1. 针对TestObject1类产生的对象,利用jol查看64位系统java对象(空对象),默认开启指针压缩,总大小显示16字节,前12字节为对象

      额外拓展说明

        • OFFSET:偏移地址,单位字节;
        • SIZE:占用的内存大小,单位为字节;
        • TYPE DESCRIPTION:类型描述,其中object header为对象头;
        • VALUE:对应内存中当前存储的值,二进制32位;

      2. 针对TestObject2类产生的对象,利用jol查看64位系统java对象(非空对象),默认开启指针压缩,总大小显示24字节,前12字节为对象

 

 

 

      3. 关闭指针压缩后:-XX:-­UseCompressedOops(元数据指针的压缩没了,恢复为8字节,进而导致有可能需要填充的变为不需要填充,或者需要填充更多)

 

 

 

  验证问题:例子中的对象占多少个字节?

    对象代码展示

class TestObject3{
    private int[] a = new int[6];
    private String[] b;
    private boolean flag;
}

    对象分析

      以之前分析可得,在开启压缩条件下,该对象为非数组对象,所以 Mark Word为8字节+Klass Pointer(元数据指针)为4字节+数组长度0字节。

      可得对象头为12字节,然后实例数据:布尔值1字节+int数组对象4字节+String数组对象4字节,故实例数据9字节。

      合计21字节,再依据对象字节数要为8的整数倍,所以应为24字节,填充3字节

    分析验证

 

 

 

    针对数组对象的分析

      代码展示

public class ObjectJolTest {
    public static void main(String[] args) {
        Object obj4 = new int[7];
        System.out.println(ClassLayout.parseInstance(obj4).toPrintable());
    }
}

      对象分析

 

posted @ 2022-09-18 18:45  忧愁的chafry  阅读(204)  评论(0编辑  收藏  举报