“new Object() 到底占多少内存?” 这道题看似简单,实则考察了面试者对 JVM 对象内存布局、指针压缩(Compressed Oops) 以及 内存对齐(Padding) 的理解。
1. 核心概念
在 HotSpot 虚拟机中,对象在堆内存中的存储布局分为三块:
- 对象头(Header):包含 Mark Word 和类型指针(Klass Pointer)。
- 实例数据(Instance Data):对象真正存储的有效信息(空对象这部分为 0)。
- 对齐填充(Padding):JVM 要求对象的大小必须是 8 字节的整数倍,不足的需要补齐。
2. 内存计算(64位 JVM)
我们主要讨论 64 位 JVM,因为 32 位现在已经很少见。64 位 JVM 有两种情况:开启指针压缩(默认开启)和 关闭指针压缩。
2.1 情况一:开启指针压缩(默认)
JVM 参数:-XX:+UseCompressedOops(JDK 1.6 update 14 后默认开启)。
- Mark Word:8 字节。
- Klass Pointer:被压缩为 4 字节。
- 实例数据:0 字节。
- 对齐填充:目前总共
8 + 4 = 12字节。为了满足 8 字节对齐,需要填充 4 字节。
总计:8 + 4 + 0 + 4 = 16 字节。
2.2 情况二:关闭指针压缩
JVM 参数:-XX:-UseCompressedOops。
- Mark Word:8 字节。
- Klass Pointer:不压缩,占用 8 字节。
- 实例数据:0 字节。
- 对齐填充:目前总共
8 + 8 = 16字节。正好是 8 的倍数,不需要填充。
总计:8 + 8 + 0 + 0 = 16 字节。
有趣结论:无论是否开启指针压缩,一个空对象在 64 位 JVM 下通常都占用 16 字节。但内部结构不同(前者有填充,后者无)。
3. 验证工具:JOL
口说无凭,我们可以使用 OpenJDK 提供的 JOL (Java Object Layout) 工具来打印对象内存布局。
引入依赖:
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.16</version>
</dependency>
测试代码:
import org.openjdk.jol.info.ClassLayout;
public class ObjectSizeTest {
public static void main(String[] args) {
Object obj = new Object();
System.out.println(ClassLayout.parseInstance(obj).toPrintable());
}
}
输出结果(开启指针压缩):
java.lang.Object object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x0000000000000001 (non-biasable; age: 0)
8 4 (object header: class) 0xf80001e5
12 4 (object alignment gap)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
可以看到 Instance size: 16 bytes,并且最后有 4 字节的 alignment gap(对齐填充)。
4. 总结
面试回答总结:
在 64 位 HotSpot 虚拟机中,一个空的
new Object()对象通常占用 16 字节。计算公式:
对象头 + 实例数据 + 对齐填充。
- 开启指针压缩(默认):Mark Word (8) + Klass Pointer (4) + Padding (4) = 16 字节。
- 关闭指针压缩:Mark Word (8) + Klass Pointer (8) + Padding (0) = 16 字节。
这里的关键点在于理解 对齐填充 的作用:JVM 强制要求对象起始地址必须是 8 字节的整数倍,因此对象大小也必须是 8 的倍数。