- 适用于 JDK 24 的 GraalVM(最新)
- 适用于 JDK 25 的 GraalVM(早期访问)
- 适用于 JDK 21 的 GraalVM
- 适用于 JDK 17 的 GraalVM
- 存档
- 开发构建
Native Image 中的对象头大小
对象头是内存中每个对象的一部分,用于存储有关对象的元数据,其大小因 JVM 实现和特定 JVM 选项(例如压缩引用)而异。对象头的大小直接影响 Java 应用程序的内存占用,尤其是在分配大量小对象时。
在 Oracle GraalVM Native Image 中,对象头默认为 4 字节,这比在 HotSpot 上运行时要小。
例如,在启用了压缩引用的 64 位 HotSpot VM 中,一个 java.lang.Object
实例占用 16 字节(12 字节头加 4 字节填充)。使用 Oracle GraalVM Native Image,同一个对象仅占用 8 字节,从而显著节省内存。然而,在 Native Image 的情况下,对象大小很大程度上取决于所使用的垃圾收集器 (GC)、分配的实例类型以及压缩引用的状态。压缩引用使用 32 位而不是 64 位,并且在 Oracle GraalVM 中默认启用。
为了观察内存使用差异,请考虑这个使用 ThreadMXBean API 测量线程分配字节的示例应用程序。
import com.sun.management.ThreadMXBean;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
public class ObjectSize {
public static void main(String[] args) {
long threadId = Thread.currentThread().threadId();
ThreadMXBean threadMXBean = (com.sun.management.ThreadMXBean) ManagementFactory.getThreadMXBean();
long initialValue = threadMXBean.getThreadAllocatedBytes(threadId);
int count = 12 * 1024 * 1024;
ArrayList<Object> objects = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
objects.add(new Object());
}
long allocatedBytes = threadMXBean.getThreadAllocatedBytes(threadId) - initialValue;
System.out.println("Object allocation test completed: " + objects.hashCode());
System.out.println("Thread allocated " + allocatedBytes + " bytes");
}
}
该应用程序创建数百万个对象实例,并计算它们创建期间分配的总内存。该应用程序报告总分配字节数,其中包括 ArrayList
的内存和单个对象的内存。
在一台配备 16 GB 内存和 Oracle GraalVM for JDK 23 的机器上运行此应用程序,会产生以下结果。
带有压缩引用和默认 Serial GC 的 Native Image
Object allocation test completed: -718496536
Thread allocated 150995032 bytes
细分来看,这等同于
48 MB for the ArrayList
96 MB for the Objects (12 * 1024 * 1024 objects × 8 bytes)
----------------------------------------------------------
Total: 144 MB
带有压缩引用和默认 G1 GC 的 HotSpot
Object allocation test completed: -1131298887
Thread allocated 251658592 bytes
细分来看,这等同于
48 MB for the ArrayList
192 MB for the Objects (12 * 1024 * 1024 objects × 16 bytes)
------------------------------------------------------------
Total: 240 MB
主要区别在于对象头大小(4 字节头 vs 12 字节头)。请注意,两种 VM 中 ArrayList
的内存占用大致相同。然而,由于 HotSpot 上更大的对象头,数百万个单个对象的内存使用量有所不同。
总而言之,对于处理大量小对象的应用程序,Native Image 可能会提供更小的内存占用。对于 Native Image,对象头大小主要取决于所使用的 GC、分配的实例类型以及压缩引用的状态。