Java里面一个对象到底有占用多少空间,这个问题要分情况看,我们先来看Java里面最顶层的Object类,它里面只有方法,没有成员变量,也就是说通过new一个Object对象就可以知道一个Java对象最少要占用多少空间,我们通过引入jol工具来打印出对象布局:
首先添加pom依赖:
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.14</version>
</dependency>
我们先看Object:
public class JolMain {
public static void main(String[] args) {
Object obj = new Object();
String layout = ClassLayout.parseInstance(obj).toPrintable();
System.out.println(layout);
}
}
输出信息如下:
java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
看不懂没关系,但是我们注意到这么几个关键的信息
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
可以看到一个Object对象占用16个字节,其中有4个字节是不属于这个对象本身的内容,是额外填充的,至于为什么要填充,Java中的对象对齐是为了提高内存访问的效率。根据Java虚拟机规范,对象的起始地址必须是某个特定大小的倍数,通常是8字节或者更大的2的幂次方。这样做的目的是为了确保对象的字段可以按照自然对齐的方式存储,以提高内存访问的效率,我们在启动参数里加一个参数:-XX:-UseCompressedOops,同样是上面的代码,输出的结果如下:
java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 00 1c d3 17 (00000000 00011100 11010011 00010111) (399711232)
12 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
可以看到这个时候就没有额外填充4个字节了,这个UseCompressedOops意思是时否开启压缩指针,因为在java对象头里面有一个类型指针指向它所属的类型,默认情况下是开启的,这个类型指针占4个字节,那么一个默认情况下Object对象实际上只占用了12个字节,为了对齐,额外填充了4个字节,而当我们不开启指针压缩的时候,类型指针站8个字节,这个时候就不需要对齐填充了
那么在默认情况下怎么保证不进行填充呢,我们新建一个User类试试:
public class User {
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public static void main(String[] args) {
User user = new User();
String layout = ClassLayout.parseInstance(user).toPrintable();
System.out.println(layout);
}
}
我们看输出结果:没有填充
org.example.jol.User object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 54 c3 00 20 (01010100 11000011 00000000 00100000) (536920916)
12 4 int User.id 0
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
我们在user里面继续加一个long类型字段(8个字节):
public class User {
private int id;
private long age;
public long getAge() {
return age;
}
public void setAge(long age) {
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public static void main(String[] args) {
User user = new User();
String layout = ClassLayout.parseInstance(user).toPrintable();
System.out.println(layout);
}
}
输出结果:没有填充
org.example.jol.User object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 54 c3 00 20 (01010100 11000011 00000000 00100000) (536920916)
12 4 int User.id 0
16 8 long User.age 0
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
继续加一个int类型字段(4个字节):
public class User {
private int id;
private long age;
private int uid;
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
public long getAge() {
return age;
}
public void setAge(long age) {
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public static void main(String[] args) {
User user = new User();
String layout = ClassLayout.parseInstance(user).toPrintable();
System.out.println(layout);
}
}
输出结果:有填充
org.example.jol.User object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 54 c3 00 20 (01010100 11000011 00000000 00100000) (536920916)
12 4 int User.id 0
16 8 long User.age 0
24 4 int User.uid 0
28 4 (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
由此可以看到,一个java对象占用的空间是8个字节的倍数,如果实际占用空间不满足这个条件的话,就会进行填充对齐
结论:
1、Java里面一个对象最少占用16个字节
2、默认情况下,jvm开启了指针压缩,对象头中的类型指针占用4个字节,关闭指针压缩就会占用8个字节
3、java对象占用空间一般是8字节的倍数,如果不满足的话,会自动填充以达到这个对齐的目的