什么是 JVM 对象内存布局?
JVM 对象内存布局 描述了 Java 对象在堆内存中的存储结构,主要包括以下几个部分:
组成部分 | 描述 |
对象头(Object Header) | 存储哈希码、GC 分代年龄、锁状态标志、线程持有锁等信息 |
实例数据(Instance Data) | 对象中定义的字段变量(如 |
对齐填充(Padding) | 为了满足 JVM 内存对齐要求而填充的无意义字节 |
步骤一:添加 JOL 依赖(Maven)
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.17</version>
</dependency>
步骤二:查看一个空对象的内存布局
import org.openjdk.jol.vm.VM;
import org.openjdk.jol.info.ClassLayout;
public class EmptyObject {
public static void main(String[] args) {
Object o = new Object();
System.out.println(VM.current().details());
System.out.println(ClassLayout.parseInstance(o).toPrintable());
}
}
📌 输出示例(64 位 JVM,未开启压缩指针):
java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000)
8 4 (object header) e5 07 00 00 (11100101 00000111 00000000 00000000)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 4 bytes internal + 4 bytes external = 8 bytes total
说明:一个空对象竟然占用了 16 字节!
步骤三:查看一个包含字段的对象布局
public class User {
boolean flag;
int age;
String name;
}
public class FieldObject {
public static void main(String[] args) {
User user = new User();
System.out.println(ClassLayout.parseInstance(user).toPrintable());
}
}
输出示例:
com.example.User object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) ...
4 4 (object header) ...
8 4 (object header) ...
12 4 boolean User.flag false
16 4 int User.age 0
20 4 java.lang.String User.name null
24 0 (loss due to the next object alignment)
Instance size: 24 bytes
说明:字段在对象中是按类型大小对齐存储的,且顺序会影响内存占用。
步骤四:实战分析字段顺序优化
public class A {
byte b1;
int i1;
byte b2;
}
public class B {
byte b1;
byte b2;
int i1;
}
分析:
- 类 A:
b1 (1)
+ padding (3) +i1 (4)
+b2 (1)
+ padding (3) → 12 bytes - 类 B:
b1 (1)
+b2 (1)
+ padding (2) +i1 (4)
→ 8 bytes
通过调整字段顺序,可以节省 4 字节内存!