OpenJDK 17中G1GC、PGC大堆内存下存储能力退化问题咨询
问题分析:G1GC/PGC大堆内存下对象存储量退化现象
实验背景
- 实验目的:探究JVM堆内存大小变化时的对象存储行为
- 堆内存范围:1GB ~ 100GB
- 测试收集器:G1GC、ZGC、PGC
- 核心现象:G1GC和PGC在堆内存超过32GB后,可存储的
Entity对象数量随堆内存增大反而减少;ZGC无此问题
测试代码
public class Memory { // Dummy Entity representing usual data objects private static class Entity { public String name; public String detail; public Double amount; public Integer age; } // Linked list offers table inserts and helps illustrating the issue by using multiple // references per entry public static java.util.LinkedList<Entity> entities = new java.util.LinkedList<>(); // This threshold should be 2 times Xmn. It ensures the loop stops before a full GC happens. private static final int MB = 1024 * 1024; private static final int THRESHOLD = 100 * MB; public static void main(String[] args) { System.out.println("Total Memory (in bytes): " + Runtime.getRuntime().totalMemory()); System.out.println("Free Memory (in bytes): " + Runtime.getRuntime().freeMemory()); System.out.println("Max Memory (in bytes): " + Runtime.getRuntime().maxMemory()); while (true) { appendEntitiesToDataStructure(); terminateBeforeFullGCorOOMEcanHappen(); } } private static void appendEntitiesToDataStructure() { entities.add(new Entity()); } private static void terminateBeforeFullGCorOOMEcanHappen() { if (Runtime.getRuntime().freeMemory() < THRESHOLD) { System.out.println("Elements created and added to LinkedList: " + entities.size()); System.exit(0); } } }
启动命令
java -Xms${i}g -Xmx${i}g -Xmn50m memory.java >> output.txt
退化现象原因分析
1. G1GC/PGC的指针压缩机制限制
JVM默认启用指针压缩(Compressed Ordinary Object Pointers, Compressed Oops),将64位指针压缩为32位,减少内存开销并提升性能。但该机制的有效寻址上限为32GB(精确值为32GB-1字节):
- 当堆内存超过32GB时,G1GC和PGC会自动禁用指针压缩,所有对象指针从4字节扩容为8字节
Entity类包含多个引用类型字段,加上LinkedList节点的前后指针,指针扩容直接增加了每个对象的内存占用- 同时JVM内部元数据、GC相关结构也会因指针膨胀占用更多堆内存,留给业务对象的可用空间增幅远低于堆内存扩容幅度,最终导致可存储的
Entity数量不增反降
2. ZGC的架构优势
ZGC从设计上摆脱了对指针压缩的依赖:
- 它采用**着色指针(Colored Pointers)**和内存映射技术,即便在TB级超大堆下也能保持高效寻址
- 无论堆内存多大,ZGC无需切换指针大小,对象内存占用稳定,堆内存扩容后可用空间能线性提升,因此不会出现存储量退化现象
3. 固定新生代配置的放大效应
你设置的-Xmn50m(新生代固定50MB)进一步放大了问题:
- 堆超过32GB时,老年代占比极高,G1/PGC的老年代区域划分(如G1的Region)、GC根节点扫描等开销会因指针膨胀大幅增加,进一步挤占业务对象的内存空间
内容的提问来源于stack exchange,提问作者sacha.p
相关产品推荐
相关产品推荐

