游戏 GC 停顿治理:分配速率、对象存活率与增量回收
GC 长帧不仅由"垃圾太多"造成。分配速率、对象图规模、存活率、根集合和回收触发策略共同决定停顿。本文用分配阶段、标记阶段和回收阶段的证据,建立可验证的优化路径。
一、先拆 GC 周期
记录触发原因、开始结束、阶段耗时、扫描对象数、回收对象数、回收字节和回收后内存。只记录总 GC 时间无法判断是根扫描、对象遍历还是释放成本。
二、分配速率比瞬时总量更敏感
短命对象大量产生,会快速触发回收并增加 CPU。统计每秒分配字节、对象数量、尺寸类别和调用来源。高频小对象可能比少量大对象更消耗管理成本。
三、存活率
如果每次 GC 大部分对象仍然存活,频繁全量扫描收益低。检查缓存、全局引用、事件订阅和对象生命周期是否让本应短命的对象进入长寿命集合。
四、触发时机
把完整 GC 放在战斗或快速转向期间会直接制造卡顿。可以在加载、过场或自然空闲窗口执行部分工作,但不能依赖固定延时猜测空闲。触发策略应结合内存压力和场景状态。
五、增量回收的权衡
增量或分步回收把一次大停顿拆到多帧,降低峰值但增加持续 CPU 开销。设置每帧预算、最大债务和紧急回收条件;若产生速度长期高于处理能力,债务仍会累积到大停顿。
六、对象池不是万能方案
对象池能减少重复分配,却可能增加常驻内存、隐藏生命周期错误和保留大对象图。只对构造昂贵、高频且容量可控的对象使用,并监控命中率、峰值容量和闲置占用。
七、验证
比较 GC P95/P99 停顿、单位时间次数、分配速率、回收效率、常驻内存和帧时间。优化不能只把停顿转移成更高的长期内存或持续 CPU。
结语
GC 优化应从"少调用回收"升级为控制分配、生命周期和回收债务。只有看到对象从产生到存活再到释放的完整链路,才能真正消除周期性卡顿。