Nehmen Sie sich 5 Minuten Zeit, um das Speicherlayout von Java-Objekten zu verstehen

Java-Objekt

Vorwort

Wenn Sie ein neues Objekt erstellen, interessiert es Sie, wie es im Speicher aussieht und wie viel Speicher es belegt. Dieser Artikel kann Ihnen schnell die Antwort geben.
Im Beispiel im Artikel ist die Standard- JVM 64-Bit , keine Komprimierung.

Speicherlayout

Java-Speicherlayout = Objektheader + Instanzdaten + Ausrichtungspolsterung

Bilderklärung

Bild.png

1. Objektkopf

Der Objektkopf besteht aus Markword + Typzeiger + Arraylänge .
Gewöhnliches Objekt: Markierungswort + Typzeiger = 8 Bytes + 4 Bytes = 12 Bytes
Array-Objekt: Markierungswort + Typzeiger + Arraylänge = 8 Bytes + 4 Bytes + 4 Bytes = 16 Bytes

1.1 Markwort

In 64-Bit-JVM sind Markword 8-Byte- Daten. Ein Byte sind 8 Bits, Markword sind also 64-Bit -Daten.

In der 32-Bit-JVM ist das Ergebnis anders, daher gehe ich hier nicht ins Detail.

Diagramm markieren

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

Bild.png

无锁对象的markword

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

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

Bild.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
复制代码

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

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

Bild.png

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

结尾

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

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

Der obige Beispielcode: Java Memory Layout Interessierte Studenten können es herunterladen und ausführen.


Guess you like

Origin juejin.im/post/7215528103467515964