objet Java
avant-propos
Lorsque vous créez un objet, si vous êtes intéressé par son apparence en mémoire et la quantité de mémoire qu'il occupe. Cet article peut vous donner la réponse rapidement.
Dans l'exemple de l'article, la JVM par défaut est 64 bits , sans compression.
disposition de la mémoire
Disposition de la mémoire Java = en-tête d'objet + données d'instance + remplissage d'alignement
Explication de l'image
1. En-tête d'objet
L'en-tête de l'objet est composé de markword + pointeur de type + longueur de tableau .
Objet ordinaire : markword + pointeur de type = 8 octets + 4 octets = 12 octets
Objet tableau : markword + pointeur de type + longueur de tableau = 8 octets + 4 octets + 4 octets = 16 octets
1.1 mot-clé
Dans JVM 64 bits, markword est une donnée de 8 octets . Un octet est de 8 bits, donc le mot de marque est une donnée de 64 bits .
Dans la JVM 32 bits, le résultat est différent, je n'entrerai donc pas dans les détails ici.
diagramme de marque
不同的位数,代表含义不同。这里引用Mark Word 详解文章中的图片。
无锁对象的markword
可能没接触过的同学看不懂该图,我再举个例子。我们都知道可以通过synchronized锁对象,来做同步操作。
一个对象创建出来没有被锁过,它的markword应该是下图这样的。
其余锁的状态看法,可以参考上图。
这里需要注意,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
复制代码
上面结果的看法如下图所示:
另外,对齐填充大小如下图所示。最终对象大小,一定是8的整数倍。
至于 (alignment/padding gap) 什么情况下会出现。本人没有仔细研究,感兴趣的同学可以看下jol的源码。下图为源码中的具体位置。 ![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b7c9375573454f3ea829d0b681d755c4~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.image?)
结尾
至此,Java对象内存布局就讲完了。不知道大家还记住多少呢。如果怕看了不久就忘了,可以收藏本文,点个赞支持下作者。文章中若有错误,可以及时指正。
今年Java找工作异常艰难,希望大家都可以度过这个寒冬。加油!!!
L'exemple de code ci-dessus : Java Memory Layout Les étudiants intéressés peuvent le télécharger et l'exécuter.