Java对象
前言
当你 new 一个对象时,如果你对它在内存中,到底什么样,究竟占多大内存感兴趣。本文可以快速的给您答案。
文章中的例子,默认JVM为64位,无压缩。
内存布局
Java内存布局 = 对象头 + 实例数据 +对齐填充
图片讲解
1.对象头
对象头由,markword+类型指针+数组长度组成。
普通对象:markword+类型指针 = 8字节 + 4字节 = 12字节
数组对象:markword+类型指针 + 数组长度 = 8字节 + 4字节 +4字节 = 16字节
1.1 markword
在64位JVM中,markword是一个8字节的数据。一字节8位,所以markword是一个64位的数据。
在32位JVM中,结果不同,这里不做赘述。
markword图解
不同的位数,代表含义不同。这里引用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找工作异常艰难,希望大家都可以度过这个寒冬。加油!!!
上文的示例代码:Java内存布局 感兴趣的同学,可以下载运行一下。