← 中文资源

简体中文版本

凭证据调 Node 堆与 GC 参数

绘制负载下的堆曲线与 GC 暂停,识别过早晋升与新生代失衡,再有的放矢设参数。

阅读英文原文

目标

在过早晋升的服务上盲目调大堆上限,只会让大 GC 更稀少也更巨大,恰恰背道而驰。GC trace 能告诉你身处哪个剧本:清道太频、老生代长得太快、还是碎片化。每个剧本对应一个不同的最小修复。先读曲线再动参数,并在同样的负载下保留前后两份日志作决策证据。

步骤要点

  1. 固定负载窗口内用 –trace-gc 记录 gc.log,统计 Scavenge 与 Mark-Compact 的次数,看最后几次大回收的暂停时长。
  2. 提取每次大 GC 前的堆大小画锯齿曲线:持续爬升说明短命对象被过早晋升,平稳说明活跃集就是那么大。
  3. 把慢请求的时间戳与 gc.log 里大 GC 的时间戳对齐,证明用户可感的卡顿确实来自 GC 暂停。
  4. 按诊断施加最小参数集:晋升过快就加大 semi-space,大暂停太疼就封顶 old-space 换高频小额,碎片化才考虑压缩策略。
  5. 同一负载重跑同款测量,把前后两组计数与暂停数字并排记录,随部署配置一起存档说明取舍理由。

排错提示

堆平稳而 RSS 仍涨说明内存在 V8 堆外,Buffer、worker 或原生插件,先看 process.memoryUsage().external 再谈堆参数;加大 semi-space 反而更糟是请求里分配了长命对象,回退参数改修分配点或封顶老生代;调参后大暂停依旧说明大头是长命对象图,给缓存加 TTL 与 LRU 上限,参数救不了代码执意持有的活跃集。

适用边界

GC 参数在暂停频率与暂停时长之间做交易,不存在同时改善两个方向的设置;trace 参数本身有开销,选定配置后要关掉 trace 复测,trace 只留给诊断期。

代码示例

gc.log before (200 rps, 5 min):
  Scavenge 4,113x   avg pause 1.8 ms   (healthy)
  Mark-Compact   22x   avg pause 310 ms  (users feel these)
  heap before majors: 310 -> 480 -> 690 -> 905 Mb   (premature promotion)

# fix: give the young generation room to let request-scoped
# objects die young
node --max-semi-space-size=64 dist/server.js

gc.log after:
  Scavenge  980x   avg pause 2.9 ms
  Mark-Compact    6x   avg pause  41 ms   (7.5x shorter)
  heap before majors: 320 -> 340 -> 345 Mb   (stable)