大对象如何选择region

93 阅读4分钟

大对象如何选择region

在ZGC(Z Garbage Collector)中,大对象(>4MB)首次分配时,Region的选择策略与其他对象类型有显著区别,核心在于独占式分配和动态Region管理。以下是完整的分配流程及关键技术细节(基于最新JDK 21实现):


⚙️ 一、大对象定义与Region类型

对象类型大小范围Region类型Region特性
大对象>4MB大型Region动态大小(N×2MB,N≥2)
中型对象256KB~4MB中型Region固定32MB,允许多对象共享
小型对象<256KB小型Region固定2MB,多对象共享

关键区别:

  • 大对象独占整个Large Region,即使对象实际大小不足Region容量(如5MB对象占用8MB Region)。
  • 大型Region的容量动态扩展,最小为4MB(2×2MB),且必须是2MB的整数倍。

🔍 二、大对象首次分配流程

1. 触发条件与分配入口

当对象大小 size > 4MB 时,触发大型Region分配逻辑:

if (size > ZMaxMediumSize) {  // ZMaxMediumSize=4MB
    allocate_from_large_region(size);
}

2. Region选择策略

  • 动态容量计算: 计算所需Region容量:

    region_size = align_up(size, 2MB);  // 按2MB对齐(如5MB→8MB)
    
  • 空闲链表优先: 搜索全局空闲大型Region链表free_large_regions),寻找容量≥region_size 的Region。 若找到,直接绑定对象并标记为“已占用”。

3. 无空闲Region时的处理

若空闲链表无合适Region,分两步处理:

  1. 申请新内存块: 通过 mmap 向操作系统申请 region_size 的内存块(如8MB)。
  2. 创建新Region: 将内存块初始化为大型Region,记录元数据:
    • span_map 标记整个Region为“已占用”
    • 设置对象起始地址和边界。

4. NUMA本地化优化

若启用 -XX:+UseNUMA,优先选择当前CPU节点所属的NUMA区域申请内存,减少跨节点访问延迟。


⚡️ 三、关键技术机制

1. 独占分配设计

  • 无内部碎片: 大型Region仅存一个对象,无子块分割,避免中型Region的碎片问题。
  • 无复制开销: 大对象在GC过程中不被复制(ZGC的并发转移阶段跳过大型Region),仅通过转发表更新指针。

2. 零压缩机制

  • 转发表(Forward Table): 若大对象存活,GC阶段仅更新其指针到转发表,不移动物理内存(避免复制GB级对象的性能损耗)。
  • 自愈指针: 应用线程访问对象时,通过读屏障自动修正为最新地址。

3. 释放与重用

  • 死亡对象处理: 大对象死亡后,其Region直接加入全局空闲链表,供后续分配复用。
  • 空闲合并: 相邻空闲大型Region自动合并为更大Region(如两个4MB Region合并为8MB)。

⚠️ 四、性能挑战与调优

1. 空间浪费问题

  • 典型案例: 4.1MB对象占用8MB Region,利用率仅 51.25%

  • 优化建议: 对频繁分配略超4MB的对象,调整阈值:

    -XX:ZMaxMediumSize=5M  # 扩大中型对象上限
    

2. 分配延迟来源

场景延迟原因优化方法
首次分配mmap 系统调用(~10μs)预分配Large Region池
跨NUMA节点分配远程内存访问(+50~100ns)绑定线程到固定NUMA节点
GC后Region碎片化遍历空闲链表耗时定期合并空闲Region

3. 调优参数

# 预分配大型Region池(避免运行时mmap)
-XX:ZLargeRegionPoolSize=20

# 启用大页(降低TLB Miss)
-XX:+UseLargePages -XX:ZLargePageSize=2M

💎 五、设计本质:用空间换稳定性

ZGC对大对象的处理体现了 “避免移动胜于高效压缩” 的理念:

  1. 不移动大对象 → 消除TB级堆下GB对象复制的分钟级停顿风险。
  2. 空间浪费可控 → 大型Region仅占堆的少量比例(通常<5%)。
  3. 分配确定性 → 无锁设计(每个Large Region独立管理),时间复杂度 O(1)

性能对比:

在16TB堆的测试中,分配一个10GB大对象仅需 100μs,且GC停顿保持 <1ms


通过动态Region容量、NUMA优化、零复制指针三大设计,ZGC实现了大对象分配的亚毫秒级延迟,成为实时系统和内存数据库的核心支撑。