为什么堆设成 32 GB 以上对象反而变大?压缩指针的边界在哪里,关掉它每个对象多出多少字节?这篇用 PrintFlagsFinal、堆地址日志和分配字节数把这些数字逐个量出来,并对照 JDK 27 默认开启的紧凑对象头。
-Xmx1g MaxHeapSize=1024 MB UseCompressedOops=true UseCompressedClassPointers=true ObjectAlignmentInBytes=8 -Xmx4g MaxHeapSize=4096 MB UseCompressedOops=true UseCompressedClassPointers=true ObjectAlignmentInBytes=8 -Xmx16g MaxHeapSize=16384 MB UseCompressedOops=true UseCompressedClassPointers=true ObjectAlignmentInBytes=8 -Xmx31g MaxHeapSize=31744 MB UseCompressedOops=true UseCompressedClassPointers=true ObjectAlignmentInBytes=8 -Xmx31900m MaxHeapSize=31904 MB UseCompressedOops=true UseCompressedClassPointers=true ObjectAlignmentInBytes=8 -Xmx32g MaxHeapSize=32768 MB UseCompressedOops=false UseCompressedClassPointers=true ObjectAlignmentInBytes=8 -Xmx40g MaxHeapSize=40960 MB UseCompressedOops=false UseCompressedClassPointers=true ObjectAlignmentInBytes=8 -Xmx40g -XX:ObjectAlignmentInBytes=16: UseCompressedOops=true -Xmx60g -XX:ObjectAlignmentInBytes=16: UseCompressedOops=true -Xmx64g -XX:ObjectAlignmentInBytes=16: UseCompressedOops=false -Xmx70g -XX:ObjectAlignmentInBytes=16: UseCompressedOops=false -Xmx4g -XX:-UseCompressedOops:UseCompressedOops=false,UseCompressedClassPointers=true
-Xmx1g 堆起始地址 0x0000000125800000,压缩指针模式「Zero based」,移位 3 位 -Xmx4g 堆起始地址 0x0000007000800000,压缩指针模式「Non-zero disjoint base: 0x0000007000000000」,移位 3 位 -Xmx16g 堆起始地址 0x0000007b70800000,压缩指针模式「Non-zero based: 0x0000007b70000000」,移位 3 位 -Xmx31g 堆起始地址 0x0000007ff9000000,压缩指针模式「Non-zero based: 0x0000007ff8000000」,移位 3 位 -Xmx33g 日志里没有 Compressed Oops 模式这一行(没有启用压缩指针)
[开启(默认)]
new Object() 16 字节;空类 16;1 个引用字段 16;1 个 int 字段 16;Node(2 个引用 + 1 个 Object + 1 个 int)32
new Object[0] 16 字节;new Object[1] 24;new Object[10] 56;new int[0] 16;new int[10] 56;new byte[1] 24
new Integer(通过 Integer.valueOf(1000))16 字节;new Long 24;new String("") 对象本身 24
[-XX:-UseCompressedOops]
new Object() 16 字节;空类 16;1 个引用字段 24;1 个 int 字段 16;Node(2 个引用 + 1 个 Object + 1 个 int)40
new Object[0] 16 字节;new Object[1] 24;new Object[10] 96;new int[0] 16;new int[10] 56;new byte[1] 24
new Integer(通过 Integer.valueOf(1000))16 字节;new Long 24;new String("") 对象本身 32
[-XX:-UseCompressedOops -XX:-UseCompressedClassPointers]
new Object() 16 字节;空类 16;1 个引用字段 24;1 个 int 字段 24;Node(2 个引用 + 1 个 Object + 1 个 int)48
new Object[0] 24 字节;new Object[1] 32;new Object[10] 104;new int[0] 24;new int[10] 64;new byte[1] 32
new Integer(通过 Integer.valueOf(1000))24 字节;new Long 24;new String("") 对象本身 32 [开启(默认)]
500 万个 Node(next/prev/data/v)+ 500 万元素的 Node[]:GC 之后堆占用 155 MB;建立耗时 120 ms;遍历链表一遍(15 次)中位 10.1 ms、最小 7.4 ms、最大 10.9 ms(校验和 187499962500000)
[-XX:-UseCompressedOops]
500 万个 Node(next/prev/data/v)+ 500 万元素的 Node[]:GC 之后堆占用 194 MB;建立耗时 221 ms;遍历链表一遍(15 次)中位 5.7 ms、最小 5.6 ms、最大 7.2 ms(校验和 187499962500000)
[JDK 27 开启(默认)]
500 万个 Node(next/prev/data/v)+ 500 万元素的 Node[]:GC 之后堆占用 117 MB;建立耗时 112 ms;遍历链表一遍(15 次)中位 9.4 ms、最小 7.1 ms、最大 10.5 ms(校验和 187499962500000)
[JDK 27 -XX:-UseCompressedOops]
500 万个 Node(next/prev/data/v)+ 500 万元素的 Node[]:GC 之后堆占用 194 MB;建立耗时 132 ms;遍历链表一遍(15 次)中位 5.7 ms、最小 5.6 ms、最大 7.3 ms(校验和 187499962500000)
对照 JDK 27:UseCompressedOops=true,UseCompressedClassPointers=None,UseCompactObjectHeaders=true
JDK 27 默认配置下的对象大小:
new Object() 8 字节;空类 8;1 个引用字段 16;1 个 int 字段 16;Node(2 个引用 + 1 个 Object + 1 个 int)24
new Object[0] 16 字节;new Object[1] 16;new Object[10] 56;new int[0] 16;new int[10] 56;new byte[1] 16
new Integer(通过 Integer.valueOf(1000))16 字节;new Long 16;new String("") 对象本身 24
JDK 27 加 -XX:+UseCompactObjectHeaders:
new Object() 8 字节;空类 8;1 个引用字段 16;1 个 int 字段 16;Node(2 个引用 + 1 个 Object + 1 个 int)24
new Object[0] 16 字节;new Object[1] 16;new Object[10] 56;new int[0] 16;new int[10] 56;new byte[1] 16
new Integer(通过 Integer.valueOf(1000))16 字节;new Long 16;new String("") 对象本身 24