한국어English日本語简体中文繁體中文DeutschไทยTiếng ViệtРусскийPortuguês (Brasil)EspañolBahasa Indonesia

游戏卡顿白皮书 › L10 内存

GC 颠簸(堆余量不足) GC thrashing (heap nearly full)

原因 ID mem-gc-thrash · 主责 研发团队·服务器开发 · 配合 运维团队·系统运维

在含图示和实验的完整版中打开此卡片 →

存活数据接近堆上限时,GC 即使运行也几乎回收不到东西,于是不停地反复执行。

起因 活动人数增加或内存泄漏,让存活数据逼近堆上限 → 结果 GC 只能回收一点点,紧接着又是 Full GC,CPU 大部分时间都花在 GC 上 → 画面表现 全服几分钟内反复出现慢动作、卡住,最后因内存不足而终止

症状
慢动作, 卡住, 掉线
因素
停顿
谁会遇到
全服
何时出现
晚高峰, 人多的时候, 开得越久越严重
负责方
主责 研发团队·服务器开发 · 配合 运维团队·系统运维
研发团队要做的事
堆大小要比高峰时的存活数据留足余量(通常 2 倍以上),减少长期引用的数据和泄漏。
运维团队要做的事
为 GC 时间占比设置告警,不要硬撑、尽早重启,选用内存充裕的实例以便调大堆。
数值参考
GC 占用超过 10% 的运行时间,一般就视为危险信号。Java 的部分 GC 如果把 98% 的时间花在 GC 上却几乎回收不到内存,就会抛出内存不足错误。
监控图上
触顶后走平 · GC 后的堆、GC 时间占比
查看位置
看 GC 后剩余的堆离最大堆有多近,以及花在 GC 上的时间占比。Java 看 -Xlog:gc 行里的“GC 后(堆大小)”和 Pause Full 行出现的频率,.NET 看 dotnet-counters(.NET 8 及以下为 % Time in GC since last GC,.NET 9 起看 dotnet.gc.pause.time 的增量),Go 看 GODEBUG=gctrace=1 各行之间的间隔
确认依据
GC 刚结束堆仍接近最大值,Full GC 接连执行,GC 时间占比明显高于平时(通常超过 10%)。这段时间全服 tick 一起变慢
排除依据
GC 后堆还有余量、只是停顿长,属于 GC 类型或配置问题,看“服务器 GC 全局停顿”(mem-gc)
确认手段
运维工具即可确认(无需游戏代码)

出处

  1. The Parallel Collector Oracle
    Parallel GC 如果超过 98% 的总时间都在做 GC,却只回收不到 2% 的堆,就抛出 OutOfMemoryError
  2. Garbage-First Garbage Collector Tuning Oracle
    G1 默认(GCTimeRatio=12)按照 GC 时间不超过总时间约 8% 的目标确定堆大小;堆占用过高引发的 Full GC,可在日志中通过 Pause Full (G1 Compaction Pause) 找到
  3. A Guide to the Go Garbage Collector Go
    默认 GOGC=100 时目标堆约为存活堆的 2 倍;贴近内存上限时 GC 会不停运行,形成颠簸(thrashing);用 GODEBUG=gctrace=1 输出 GC 跟踪信息
  4. Garbage Collector Implementation Oracle
    -Xlog:gc 行包含 GC 类型(Pause Young、Pause Full)、“GC 前占用->GC 后占用(堆大小)”和停顿时间
  5. dotnet-counters diagnostic tool .NET
    .NET 9 起显示为 dotnet.gc.pause.time,.NET 8 及以下显示为 % Time in GC since last GC

相关原因

同一层:L10 内存

其他层中同样导致“慢动作”的原因

查看含图示和实验的原卡片