G1如何给新建的对象选择Region
我们已经了解了Region内部的结构和关键指针G1的Region的内部结构 ,接下来将介绍G1如何给对象选择Region。
一、Region如何给小对象分配内存
1.按策略选择region
2.使用指针碰撞分配空间
sequenceDiagram
分配请求->>Region: 申请大小S
Region->>Top: 读取当前值
Top->>计算: Top + S
计算->>"End": 比较End值
alt 空间足够
"End"-->>Region: 返回成功
Region->>Top: 更新Top值
Region-->>请求: 返回分配地址
else 空间不足
"End"-->>空闲链表: 调用空闲链表分配,剩余空间继续加入空闲链表
空闲链表-->>Region: 返回碎片地址或失败
end
二、G1如何给对象分配Region
1.Region管理结构
classDiagram
class Region {
+type: RegionType
+top_ptr: address
+end_ptr: address
+free_list: FreeBlock*
+bitmap: BitMap
+prev: Region 逻辑顺序上一个region的指针
+next: Region 逻辑顺序下一个region的指针
}
class RegionManager {
+eden_list: List~Region~
+survivor_list: List~Region~
+old_list: List~Region~
+humongous_list: List~Region~
+free_list: List~Region~
}
RegionManager --> "*" Region
2.按规则选择不同类型的Region
G1 垃圾收集器使用多层筛选策略从 Region 列表中选择最适合存储新对象的 Region,其核心是价值最大化原则。以下是完整的 Region 选择机制:
graph TD
A[对象分配请求] --> B{对象大小}
B -->|小对象| C[选择Eden Region]
B -->|大对象| D[选择Humongous Region]
C --> E[Region筛选]
D --> F[连续空间检查]
E --> G[快速路径or空间相邻or价值评估]
G --> H[最优选择]
F --> I[空间连续性]
I --> J[分配执行]
style G white-space:normal
3.小对象分配:Eden Region 选择
sequenceDiagram
G1分配器->>Region管理器: 请求Eden Region
Region管理器->>筛选: 过滤可用Region
筛选->>评估: 计算Region价值
评估->>排序: 优先获取最近释放的region,<br/>如果没有就获取空间相邻的,<br/>如果失败其次是通过价值模型评估分数获取最高分
排序-->>G1分配器: 返回最佳Region
G1分配器->>TLAB: 从Region分配空间
源码实现(OpenJDK 17)
HeapWord* G1CollectedHeap::attempt_allocation(size_t size) {
// 快速路径:尝试最近释放的Region
HeapRegion* hr = _allocator->attempt_allocation_using_recent_region(size);
if (hr != nullptr) {
return hr->allocate(size);
}
// 次级路径:空间邻近Region
hr = _allocator->attempt_allocation_near(size);
if (hr != nullptr) {
return hr->allocate(size);
}
// 慢速路径:完整价值模型
return attempt_allocation_slow(size);
}
3.1 为什么实现多种策略
这就是理论模型与实际优化的差异,价值模型拥有最好的空间局部性 ,而快速路径拥有最好的分配速率,价值模型需要耗时计算所以在分配速率上低效,快速路径在实践中大概率是价值模型中的高分对象,也就是说,快速路径大概率拥有比较好的空间局部性。
graph TD
A[空间局部性] --> B[定义]
A --> C[价值模型]
A --> D[快速路径]
B --> B1["数据物理位置邻近性"]
C --> C1["追求最优局部性"]
D --> D1["追求最快分配"]
3.2设计哲学
graph LR
优化目标 --> 时间[分配速度]
优化目标 --> 精度[最优布局]
矛盾 --> 方案[分层策略]
方案 --> 快[快速路径保速度]
方案 --> 精[价值模型保布局]
时间局部性原理
graph LR
最近释放 --> 假设["刚释放的Region很可能再次使用"]
假设 --> 依据[程序访问局部性]
依据 --> 效果[缓存命中率提升]
style 假设 white-space:normal
空间局部性原理
sequenceDiagram
程序访问->>内存: 访问对象A
内存-->>缓存: 加载A及邻近区域
后续访问->>缓存: 访问邻近对象B
缓存-->>加速: 命中缓存
热度继承原理
gantt
title Region热度继承
dateFormat s
axisFormat %ss
section RegionA
高频分配 : 0, 3
释放 : 3, 1
section 新分配
选择RegionA : 4, 1
继承热度 : 5, 1
3.3 实践数据
| 策略 | 触发频率 | 平均延迟 | 局部性得分 |
|---|---|---|---|
| 快速路径 | 80% | 40ns | 0.85 |
| 空间邻近 | 15% | 60ns | 0.92 |
| 价值模型 | 5% | 150ns | 0.97 |
3.4 价值评估模型
3.5 region评估流程
sequenceDiagram
participant 分配线程
participant Region管理器
participant NUMA管理器
分配线程->>Region管理器: 请求新TLAB
Region管理器->>筛选: 获取可用Eden Regions
筛选-->>Region管理器: 返回候选列表
Region管理器->>NUMA管理器: 查询线程NUMA节点
NUMA管理器-->>Region管理器: 返回节点ID
Region管理器->>计算: 计算每个Region得分
计算->>公式: 得分=局部性×0.6 + NUMA×0.3 + 热度×0.1
计算-->>Region管理器: 返回得分排序
Region管理器->>选择: 选择最高分Region
选择-->>分配线程: 返回Region地址
3.6 热度(Allocation Heat)
热度(Allocation Heat) 是一个关键指标,它衡量了 Region 在近期被用于对象分配的活跃程度。这个指标通过智能预测未来分配模式,优化新对象的分配位置。
graph TD
A[分配热度] --> B[定义]
A --> C[计算方式]
A --> D[核心作用]
B --> B1["Region被用于分配的频率"]
C --> C1["基于历史分配次数"]
D --> D1["预测未来分配热点"]
3.7 NUMA亲和度
在 G1 垃圾收集器中,NUMA 亲和度(NUMA Affinity) 是衡量内存 Region所在内存 与当前 CPU 节点的物理距离的指标,其计算涉及硬件拓扑感知和运行时动态绑定。非G1独有技术,详情见NUMA(Non-Uniform Memory Access,非一致内存访问) ,对于大内存多颗cpu的服务器具有明显优化。
3.8 小对象复用Region
需要注意的是,G1如果从空闲链表中,选择Region之后,会将Region从空闲链表移除,并标记为eden类型,然后加入到RegoinManger中的eden区列表中。
并不是所有小对象都会从空闲链表选择Region,或者说小对象的分配第一层,是先遍历所有eden类型的region,这个所有eden类型的region是G1顶层结构的一个列表,通过遍历eden列表寻找eden列表里面空闲的空间,找不到再去空闲链表通过策略去选择一个新的region作为eden区,并用于对象分配。
当新生代分配不了内存,g1也会使用大对象使用的区间树来分配内存。填充大对象分配和的region剩余部分。
4.大对象分配:Humongous Region 选择
4.1 连续空间选择算法
graph TD
A[大对象分配] --> B{对象大小}
B -->|小于等于 RegionSize| C[单个Region分配]
B -->|大于 RegionSize| D[多Region分配]
C --> E[查找空闲Region]
D --> F[计算所需Region数]
F --> G[查找连续空闲Region]
E --> H[优先尾部Region]
G --> I[尾部连续空间优先]
H --> J[标记为Humongous]
I --> K[标记为连续Humongous]
4.2 尾部优先策略
graph LR
尾部优先 --> 原因[减少内存碎片]
原因 --> 机制[优先使用堆尾部空间]
机制 --> 优势[保持低地址空间连续]
优势 --> 效果[减少Full GC触发]
4.3 连续空间查找算法
sequenceDiagram
分配请求->>Region管理器: 申请大对象空间
Region管理器->>空闲列表: 获取空闲Region列表
空闲列表->>排序: 按地址降序排列
排序->>查找: 从尾部向前扫描
查找->>连续检查: 验证连续N个Region
连续检查-->>Region管理器: 返回可用地址
Region管理器-->>分配请求: 分配成功
4.4 单个 Region 分配
flowchart TD
开始 --> 计算[计算所需Region数=1]
计算 --> 查找[尾部优先查找空闲Region]
查找 --> 分配[分配并标记为StartsHumongous]
分配 --> 记录[加入全局Humongous列表]
这里对大对象的region分配不做详细讲解,详细请见G1如何管理空闲空间的