G1停顿预测模型核心是用EWMA动态估算Region回收耗时,结合垃圾密度计算“性价比”排序选CSet;EWMA因权重向近期倾斜、衰减历史影响、计算轻量,更适配Java内存行为突变;各操作维度独立维护EWMA,合成预期总耗时;预测失准主因并发标记未完成、Survivor过小、大对象分配及α值不当。

G1 停顿预测模型的核心,是用指数加权平均值(EWMA)动态估算每个 Region 的回收耗时,从而在目标停顿时间内选出“性价比最高”的回收集(CSet)。它不是靠固定公式拍板,而是持续学习最近的 GC 行为,让预测更贴合当前运行状态。
为什么选 EWMA 而不是简单平均?
Java 应用的内存行为常有突变——比如突发流量导致对象晋升激增、大对象批量分配、或 Survivor 区快速溢出。简单平均会把几天前平稳期的数据和刚发生的抖动等同看待,预测滞后严重。EWMA 则天然适配这种动态场景:
- 给最近几次的 Region 回收耗时赋予更高权重,响应更快
- 历史数据影响随时间指数衰减,避免被旧模式长期绑架
- 计算轻量,仅需上一次 EWMA 值和本次实测值,适合高频更新
EWMA 在 G1 中的具体实现逻辑
G1 对每类操作(如扫描一个 Region、复制一个对象、更新引用)都维护独立的 EWMA 估计值。以“扫描一个 Old Region 的平均耗时”为例,其更新公式与金融中常用形式一致:
scan_time_ewmat = (1 − α) × actual_scan_timet + α × scan_time_ewmat−1
其中 α 是衰减因子,默认值通常在 0.7–0.95 区间(JDK 实现中常取 0.9),意味着约 70% 的权重来自最新观测,其余由历史趋势平滑承接。
实际中,G1 不只算“扫描”,还会分别跟踪:
- 每个 Region 的存活对象数量(用于预估复制开销)
- 对象跨 Region 引用更新成本(尤其是 Remembered Set 处理时间)
- Humongous 对象清理的额外延迟
这些维度各自用 EWMA 累积,最终合成单个 Region 的“预期总耗时”。
预测如何转化为回收决策?
有了各 Region 的 EWMA 耗时预估,G1 还要结合“能回收多少空间”来排序。它真正优化的目标是:单位时间换最大可用内存。所以流程是:
- 并发标记阶段结束后,G1 得到一批 Old Region 的垃圾密度(即可回收空间占比)
- 对每个候选 Region,用 EWMA 耗时 ÷ 垃圾密度 → 得出“性价比分”
- 按性价比从高到低排序,依次加入 CSet,直到预计总耗时逼近 MaxGCPauseMillis
注意:这个“预计总耗时”本身也是多个 EWMA 分项的加权和,不是直接相加,而是模拟执行路径后估算的综合停顿。
调参与现实偏差的关键点
EWMA 再灵敏,也受限于输入质量。以下情况会让预测明显失准:
- 并发标记未完成时触发 Mixed GC:Old Region 垃圾量未知,只能靠粗略估算,易高估收益、低估复制量
- Survivor 区过小:大量对象提前晋升,Old Region 存活率升高,但 EWMA 仍按历史低存活率建模,导致复制远超预期
- 突发大对象分配:Humongous Region 清理不走常规路径,其耗时不在主 EWMA 模型中,容易造成停顿“黑箱”
- α 设得过大(如 0.99):模型太“恋旧”,无法及时响应新负载;设得太小(如 0.3):又过于敏感,把噪声当趋势
因此,JDK 默认 α 值是经验平衡结果,一般不建议手动调整,而应优先优化对象生命周期分布和堆结构。

















