对象和数组在JVM如何在堆中布局?更常见地问法就是对象头都包含哪些信息
在JVM中对象和数组尽管都是连续的内存块。但在堆内存中的布局方式有些不同。
对象的组成
对象在JVM中可以分为三个部分,对象头(Header),实例数据(Instance Data)和对其填充(Padding)
那么在对象头中,有两个最重要地部分
- mark word,HotSpot JVM 使用mark word来存储身份哈希码、偏向锁定模式、锁定信息和 GC 元数据。
详情可以参考https://gist.github.com/arturmkrtchyan/43d6135e8a15798cc46c
- klass 类型指针,封装了类地元信息,例如类名、其修饰符、父类类信息等。其大小在 32 位和 64 位架构中分别为 4 和 8 字节。 此外,有偏向锁的对象和正常对象的标记词是不同的。但是,这里我们只会考虑普通对象,因为从 Java 15 默认关闭偏向锁。
对象的大小
对于 Java 中表示为instanceOop的普通对象,对象头由 mark 和 klass 字以及可能的对齐 padding 组成。在对象头之后,可能有零个或多个对实例字段的引用。因此,在 64 位架构中至少有 16 个字节(8个字节的标记、4个字节的klass和另外4个字节的padding填充)。
面试中有个最基本的问题,Object o = new Object();o的大小是多少?
public class Main {
public static void main(String[] args) {
System.out.println(ClassLayout.parseInstance(new Object()).toPrintable());
}
}
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) 08 0d 00 00 (00001000 00001101 00000000 00000000) (3336)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
对于表示为 arrayOop 的数组, 对象头除了 mark、klass 和 paddings 之外还包含 4 字节数组长度。所以最少是 16 个字节(8 个字节的标记、4个字节的klass和另外4个字节的数组长度。
如果面试题换做new int[0]的大小是多少?
public static void main(String[] args) {
System.out.println(ClassLayout.parseInstance(new int[0]).toPrintable());
}
[I 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) 68 22 00 00 (01101000 00100010 00000000 00000000) (8808)
12 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
16 0 int [I.<elements> N/A
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
如果将数组长度扩大到10
[I 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) 68 22 00 00 (01101000 00100010 00000000 00000000) (8808)
12 4 (object header) 0a 00 00 00 (00001010 00000000 00000000 00000000) (10)
16 40 int [I.<elements> N/A
Instance size: 56 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
压缩引用
我们可以使用 jvm参数 -XX:-UseCompressedOops 禁用指针压缩,那么 klass 字将消耗 8 个字节而不是 4 个字节。
总结
Java对象的内存布局也算是面试中的高频题,强烈建议学习JVM源代码特别是oops部分。参考资料
往期文章
金三银四面试题(一):JVM类加载与垃圾回收
金三银四面试题(二):数据库缓存的数据一致性
金三银四面试题(三):JVM内存模型
金三银四面试题(四):Full GC 和 Minor GC
金三银四面试题(五):JVM之TLAB