Java对象的内存布局

  • Post author:
  • Post category:java


对象内存存储布局概述

在JVM虚拟机中,对象的储存布局可以为分为三个部分,

对象头(Object Header)



实例数据(Instance Data)



对齐填充(Padding)

下面我们来具体说一下各个部分。

对象头(Object Header)

虚拟机的头部分主要包括

存储对象自身的运行时数据(Mark Word)



类型指针

,在数组对象中这两个的基础上会多一个

数组长度。

这个是对象头包含的内容。

Mark Word

Mark Word是用于存储对象自身的运行时数据。在32位和64位系统(未开启压缩指针)中不一样的。在对象不同的锁状态下,也是不同的。下面这幅图指明Mark Word的内容:

这个是我手画的,大家可以自己动手画一画。

类型指针(Klass Word)

Klass Word即对象指向它的类型元数据的指针,Java虚拟机通过这个指针来确定该对象是哪个类的实例。


如果对象是一个Java数组,对象头中还会添加一块用于记录数组长度的数据。

实例数据(Instance Data)

实例数据部分是对象真正存储的有效信息(我们在程序中定义的各中类型的字段内容),这些内容会被记录在实例数据中。

对齐填充(padding)

对齐填充仅仅起到一个占位符的作用,自动内存管理系统要求起始地址必须是8字节的整数倍。对其填充就是将对象填充至8字节的整数倍。

实验

现在我们需要导入一个依赖,提供支持

        <dependency>
            <groupId>org.openjdk.jol</groupId>
            <artifactId>jol-core</artifactId>
            <version>0.9</version>
        </dependency>

然后我们来使用 ClassLayout.parseInstance(a).toPrintable() 来查看对象的布局:


查看空Object的布局

    public static void main(String[] args) {
        Object o = new Object();
        System.out.println(ClassLayout.parseInstance(o).toPrintable());
    }

首先有对象头

的Mark Word有64bit,8bytes

,然后

有4bytes的类型指针

,因为是空Object所以没有实例数据,然后

对齐填充4bytes

。所以总结得

Object对象是16bytes


接着我们创建一个对象,来看看它的布局


首先创建一个没有属性的对象

    public static void main(String[] args) {
        B b = new B();
        System.out.println(ClassLayout.parseInstance(b).toPrintable());
    }


class B {

}

这里和创建一个Object是一样的,就不说了。


为B对象添加基本类型的属性

    public static void main(String[] args) {
        B b = new B();
        System.out.println(ClassLayout.parseInstance(b).toPrintable());
    }


class B {
    boolean  b = true;
    int anInt = 1;
    int bIn = 2;
}

为对象添加基本类型的属性,对象布局中会出现实例数据这一块,并且

实例数据是连续

的。并且除了

布尔数据占1bytes外其他的数据类型都占4bytes


为B对象添加非基本类型的数据

    public static void main(String[] args) {
        B b = new B();
        System.out.println(ClassLayout.parseInstance(b).toPrintable());
    }


class B {
    boolean  b = true;
    int anInt = 1;
    int bIn = 2;
    String s = "aaaa";
    JsonController jsonController = new JsonController();
}

为B对象填充非基本数据类型后,在实例数据模块中

会出现Padding gap

,这里是将每一个数据填充到4bytes,并且非基本类型的数据也是4bytes。

总结一下就是padding gap出现必须有两个条件:

实例数据不够4bytes,对象中存在非基本类型数据属性。


最后我们看一下数组对象

    public static void main(String[] args) {
        int[] a = new int[100];
        System.out.println(ClassLayout.parseInstance(a).toPrintable());
    }

这是一个初始化为100长度的数组对象,我们可以看到在数组对象的对象头中多一个

4bytes的记录数组长度

的模块,并且在后面已经为数组开辟好一个

每长度为4bytes的内存空间。这里的对象刚好是8bytes的倍数,所以就不需要对齐。


对象的内存布局就差不多讲到这里了,总结一下


对象内存布局主要是有Mark Word(一般情况下12bytes,数组对象16bytes)、实例数据(看对象是怎么样的决定)、对齐填充(对齐为8bytes的倍数)组成的。

感谢大家的观看,大家一起进步!



版权声明:本文为m0_47954009原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。