5 minutos, lo llevarán a comprender el diseño de la memoria de los objetos Java

objeto java

prefacio

Cuando crea un objeto nuevo , si está interesado en cómo se ve en la memoria y cuánta memoria ocupa. Este artículo puede darle la respuesta rápidamente.
En el ejemplo del artículo, la JVM predeterminada es de 64 bits , sin compresión.

diseño de memoria

Diseño de memoria Java = encabezado de objeto + datos de instancia + relleno de alineación

explicación de la imagen

imagen.png

1. Encabezado de objeto

El encabezado del objeto se compone de markword + puntero de tipo + longitud de matriz .
Objeto ordinario: palabra clave + puntero de tipo = 8 bytes + 4 bytes = 12 bytes
Objeto de matriz: palabra clave + puntero de tipo + longitud de matriz = 8 bytes + 4 bytes + 4 bytes = 16 bytes

1.1 palabra clave

En JVM de 64 bits, markword es un dato de 8 bytes . Un byte tiene 8 bits, por lo que markword es un dato de 64 bits .

En la JVM de 32 bits, el resultado es diferente, por lo que no entraré en detalles aquí.

diagrama de marcas

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

imagen.png

无锁对象的markword

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

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

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

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

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

imagen.png

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

结尾

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

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

El código de ejemplo anterior: Java Memory Layout Los estudiantes interesados ​​pueden descargarlo y ejecutarlo.


Supongo que te gusta

Origin juejin.im/post/7215528103467515964
Recomendado
Clasificación