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、分配的实例类型以及压缩引用的状态。

延伸阅读 #

联系我们