G1通过Region实现精细化内存管理:Region大小为1MB~32MB(2的幂),动态承担各代角色,跨Region引用靠RSet维护;大对象(>0.5×RegionSize)需连续分配并标记为Humongous,仅Mixed GC可回收,易触发Full GC。

G1收集器把堆内存划分为固定大小的逻辑单元——Region,不追求物理连续,而是靠标记和引用关系管理。大对象(Humongous Object)的处理不是简单“扔进老年代”,而是一套受约束的连续分配机制:只要对象大小超过单个Region容量的50%,G1就强制为其分配一组物理连续的Region,并统一标记为Humongous类型。
Region划分的核心特点
Region是G1实现精细化回收的基础单位:
- 大小由堆总容量自动计算(≈堆大小 / 2048),范围在1MB~32MB之间,且为2的整数次幂;手动设置可通过-XX:G1HeapRegionSize调整,但不宜盲目缩小(如设1MB会导致Region数量爆炸、Remembered Set元数据开销剧增)或过大(如32MB易造成空间浪费和回收滞后)
- 每个Region可动态承担Eden、Survivor、Old或Humongous角色,不再有固定边界;同一个Region内可能混存新生代与老年代对象
- Region之间无物理连续性,跨Region引用通过Remembered Set(RSet)记录,带来额外内存与写屏障开销
Humongous对象的识别与分配规则
判断依据是对象原始数据大小(如byte[]长度、String内部char[]容量),而非引用本身:
- 若对象大小 > RegionSize × 0.5,即被标记为Humongous;例如Region为4MB时,≥2MB的对象即触发该机制
- 分配跳过Eden/Survivor,直接在老年代区域中寻找足够数量的物理连续空闲Region;首个Region标记为Humongous Start,后续为Humongous Continuation
- 若找不到连续空闲段(比如需3个连续Region,但最大连续段只有2个),G1会立即触发Full GC腾空间——这是生产中Full GC突增的常见诱因
Humongous对象的回收行为
它不参与Young GC,回收时机非常受限:
- 仅在Mixed GC阶段被纳入回收集合:前提是并发标记已完成,且G1判定该Humongous Region所在的老年代区域整体具备回收价值
- JDK 8u40起支持单Region级回收(不再强求整组释放),但前提是该Region内所有对象都已不可达;实际依赖SATB标记精度,短命大对象也常因残留引用而无法及时释放
- 长期被持有引用、或连续空间持续紧张时,极易退化为Full GC;尤其当大量临时大对象(如一次读取百万行JSON转List)高频创建又快速丢弃,会反复推高Mixed GC频率甚至阻塞分配
识别与调优的关键落点
不能只看“有没有大对象”,要确认它是否构成GC瓶颈:
- 开启详细GC日志:-Xlog:gc*,gc+humongous=debug:file=gc.log:time,tags,level(JDK 10+),关注
humongous allocation、humongous region freed等关键词 - 用jstat -gc <pid>观察H(Humongous allocated)与HU(Humongous used)指标变化趋势;HU长期高位或波动剧烈是典型信号
- 调优方向聚焦三点:降低误判概率(如将RegionSize从4MB调为2MB,使原本2.1MB对象从占1个Humongous Region变为占2个普通Region);控制生命周期(改批量加载为流式分页、用DirectByteBuffer承载真正大块数据);避免碎片雪球(确保堆有足够连续空闲段,必要时适度增大-Xmx)

















