5 分で、Java オブジェクトのメモリ レイアウトを理解できます

Java オブジェクト

序文

オブジェクトを新規作成するとき、それがメモリ内でどのように見えるか、およびそれが占有するメモリの量に関心がある場合。この記事では、すぐに答えを得ることができます。
この記事の例では、デフォルトのJVM64 ビット、圧縮なしです。

メモリ レイアウト

Java メモリ レイアウト = オブジェクト ヘッダー + インスタンス データ + アラインメント パディング

写真説明

画像.png

1. オブジェクトヘッダー

オブジェクト ヘッダーは、マークワード+型ポインタ+配列の長さで構成されます
通常オブジェクト:マークワード+型ポインタ= 8 バイト + 4 バイト = 12 バイト
配列オブジェクト:マークワード+型ポインタ+配列長= 8 バイト + 4 バイト + 4 バイト = 16 バイト

1.1 マークワード

64 ビット JVM では、マークワードは8 バイトのデータです1 バイトは 8 ビットなので、マークワードは64 ビットのデータです

32 ビット JVM では結果が異なるため、ここでは詳しく説明しません。

マーク図

不同的位数,代表含义不同。这里引用Mark Word 详解文章中的图片。

画像.png

无锁对象的markword

可能没接触过的同学看不懂该图,我再举个例子。我们都知道可以通过synchronized锁对象,来做同步操作。

一个对象创建出来没有被锁过,它的markword应该是下图这样的。

画像.png 其余锁的状态看法,可以参考上图。
这里需要注意,hashCode的值,只有在调用hashcode()方法后,才会改变值。

1.2 类型指针

大小为4字节,指向方法区中,该对象的类型。

1.3 数组长度

只有当创建的是数组时,才有该部分,大小为4字节,代表当前数组的长度非数组时,不存在

即:普通对象对象头 = markword + 类型指针
数组对象 = markword + 类型指针 +数组长度


2.实例数据

一个对象的实例数据大小,等于它所有成员变量大小,以及继承类的变量的大小的和。

如果你的对象不继承任何对象,只有一个int型变量。那么你的实例数据大小就为4字节

如果对象A的父类有一个boolean类型变量,对象A有一个char类型变量。
A对象的实例数据大小 = boolean(1字节)+char(2字节

无论父类的变量是private还是public修饰,都会算在子类的实例数据中。父类的父类也会算在实例数据中。

注意一点,静态变量存放在方法区,因此不占用对象内存。

3.对齐填充

一个对象大小必须为8的整数倍。如果对象头+实例数据 = 57个字节。那么对齐填充就为7字节。对象头+实例数据+对齐填充 = 64字节。正好8的整数倍。你可以理解为对齐填充就是凑数用的。

实例讲解

下面通过一个具体例子,以及运行结果,来印证下对象内存布局是否如上图所说。
本文含有运行后的结果。大家可以直接通过代码和结果来加深印象,无需实机运行代码。

如何查看对象

引入一个三方库:jol,通过这个库,可以打印对象内存布局

implementation group: 'org.openjdk.jol', name: 'jol-core', version: '0.17'
复制代码

辅助类Student

/**
 * Author(作者):jtl
 * Date(日期):2023/3/26 19:20
 * Detail(详情):学生类
 */
public class Student extends Person{
    String name = "ZhangSan";// 对象引用:4字节
    boolean isRealMan;//boolean:1字节
    int age = 10;//boolean:1字节
    char score ='A';//char:2字节
    long time = 20230326;//long:8字节
    Student deskMate;//对象引用:4字节

    public Student() {
    }

    static int grade;

    public boolean isRealMan() {
        return isRealMan;
    }

    public void setRealMan(boolean realMan) {
        isRealMan = realMan;
    }
}
复制代码

Student的父类Person

该类为了印证,创建Student对象时,Person类的所有变量(包括私有对象),都会占用Student对象实例数据大小。

/**
 * Author(作者):jtl
 * Date(日期):2023/3/28 16:07
 * Detail(详情):父类,Person类
 */
public class Person extends Object {
    private float time = 0L;
    String country = "China";
}
复制代码

实际运行代码

这里举了5种例子,通过下文的运行结果,大家可以进行对比,加深印象。

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Student student = new Student();
        System.out.println("-----------------------------第一阶段 new 对象-------------------------------");
        System.out.println(ClassLayout.parseInstance(student).toPrintable());

        System.out.println("-----------------------------第二阶段 hashcode-------------------------------");
        student.hashCode();
        System.out.println(ClassLayout.parseInstance(student).toPrintable());



        synchronized (student){
            System.out.println("-----------------------------第三阶段 synchronized加锁-------------------------------");
            System.out.println("synchronized (student):\n"+ClassLayout.parseInstance(student).toPrintable());
        }

        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (student){
                    System.out.println("-----------------------------第四阶段 synchronized加锁-------------------------------");
                    System.out.println("new Thread:synchronized (student):\n"+ClassLayout.parseInstance(student).toPrintable());
                }
            }
        }).start();

        synchronized (student) {
            System.out.println("-----------------------------第五阶段 Student[] 数组-------------------------------");
            System.out.println(ClassLayout.parseInstance(new Student[]{new Student(), new Student(), new Student()}).toPrintable());
        }
    }
}
复制代码

内存布局

这里可以看到的是,只有调用了hashcode方法后。markword中才会修改对应位置的数据。 另外只有对象为数组时,才会有数组长度(array length) 这个选项,大小为4字节

> Task :Main.main()
-----------------------------第一阶段 new 对象-------------------------------
org.example.Student object internals:
OFF  SZ                  TYPE DESCRIPTION               VALUE
  0   8                       (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   4                       (object header: class)    0x01000be8
 12   4                 float Person.time               0.0
 16   4      java.lang.String Person.country            (object)
 20   4                   int Student.age               10
 24   8                  long Student.time              20230326
 32   2                  char Student.score             A
 34   1               boolean Student.isRealMan         false
 35   1                       (alignment/padding gap)   
 36   4      java.lang.String Student.name              (object)
 40   4   org.example.Student Student.deskMate          null
 44   4                       (object alignment gap)    
Instance size: 48 bytes
Space losses: 1 bytes internal + 4 bytes external = 5 bytes total

-----------------------------第二阶段 hashcode-------------------------------
org.example.Student object internals:
OFF  SZ                  TYPE DESCRIPTION               VALUE
  0   8                       (object header: mark)     0x00000073035e2701 (hash: 0x73035e27; age: 0)
  8   4                       (object header: class)    0x01000be8
 12   4                 float Person.time               0.0
 16   4      java.lang.String Person.country            (object)
 20   4                   int Student.age               10
 24   8                  long Student.time              20230326
 32   2                  char Student.score             A
 34   1               boolean Student.isRealMan         false
 35   1                       (alignment/padding gap)   
 36   4      java.lang.String Student.name              (object)
 40   4   org.example.Student Student.deskMate          null
 44   4                       (object alignment gap)    
Instance size: 48 bytes
Space losses: 1 bytes internal + 4 bytes external = 5 bytes total

-----------------------------第三阶段 synchronized加锁-------------------------------
synchronized (student):
org.example.Student object internals:
OFF  SZ                  TYPE DESCRIPTION               VALUE
  0   8                       (object header: mark)     0x000070000dcafac0 (thin lock: 0x000070000dcafac0)
  8   4                       (object header: class)    0x01000be8
 12   4                 float Person.time               0.0
 16   4      java.lang.String Person.country            (object)
 20   4                   int Student.age               10
 24   8                  long Student.time              20230326
 32   2                  char Student.score             A
 34   1               boolean Student.isRealMan         false
 35   1                       (alignment/padding gap)   
 36   4      java.lang.String Student.name              (object)
 40   4   org.example.Student Student.deskMate          null
 44   4                       (object alignment gap)    
Instance size: 48 bytes
Space losses: 1 bytes internal + 4 bytes external = 5 bytes total

-----------------------------第五阶段 Student[] 数组-------------------------------
[Lorg.example.Student; object internals:
OFF  SZ                  TYPE DESCRIPTION               VALUE
  0   8                       (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   4                       (object header: class)    0x0101ed10
 12   4                       (array length)            3
 16  12   org.example.Student Student;.<elements>       N/A
 28   4                       (object alignment gap)    
Instance size: 32 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

-----------------------------第四阶段 synchronized加锁-------------------------------
new Thread:synchronized (student):
org.example.Student object internals:
OFF  SZ                  TYPE DESCRIPTION               VALUE
  0   8                       (object header: mark)     0x000060000265c002 (fat lock: 0x000060000265c002)
  8   4                       (object header: class)    0x01000be8
 12   4                 float Person.time               0.0
 16   4      java.lang.String Person.country            (object)
 20   4                   int Student.age               10
 24   8                  long Student.time              20230326
 32   2                  char Student.score             A
 34   1               boolean Student.isRealMan         false
 35   1                       (alignment/padding gap)   
 36   4      java.lang.String Student.name              (object)
 40   4   org.example.Student Student.deskMate          null
 44   4                       (object alignment gap)    
Instance size: 48 bytes
Space losses: 1 bytes internal + 4 bytes external = 5 bytes total
复制代码

上面结果的看法如下图所示:

画像.png 另外,对齐填充大小如下图所示。最终对象大小,一定是8的整数倍

画像.png

至于 (alignment/padding gap) 什么情况下会出现。本人没有仔细研究,感兴趣的同学可以看下jol的源码。下图为源码中的具体位置。 画像.png

结尾

至此,Java对象内存布局就讲完了。不知道大家还记住多少呢。如果怕看了不久就忘了,可以收藏本文,点个赞支持下作者。文章中若有错误,可以及时指正。

今年Java找工作异常艰难,希望大家都可以度过这个寒冬。加油!!!

上記のサンプル コード: Java メモリ レイアウト興味のある学生は、ダウンロードして実行できます。


おすすめ

転載: juejin.im/post/7215528103467515964