Node.js 内存泄漏排查实战:从堆内存暴涨到定位根因
Node.js 服务内存暴涨导致 OOM 重启?本文记录一次真实的内存泄漏排查全过程:从监控报警、堆快照对比到定位闭包引用导致的缓存泄漏根因,并给出修复方案与日常避坑清单,适合 Node.js 后端开发者参考。
# Node.js 内存泄漏排查实战:从堆内存暴涨到定位根因
## 一、事故背景
线上 Node.js 服务稳定运行两周后,监控系统报警:进程 RSS 内存从 300MB 一路涨到 2.1GB,容器多次因 OOM 被重启。服务没有明显的流量增长,GC 日志显示老年代持续攀升且回收不掉,基本可以断定存在内存泄漏。
## 二、初步排查:确认泄漏方向
1. 查看进程内存分布,确认 heapUsed 占大头;
2. 连续抓取三次 heap dump,间隔 30 分钟;
3. 对比快照发现某个业务对象实例数量线性增长,与请求量成正比,锁定泄漏源在业务代码而非底层依赖。
## 三、定位根因:经典的闭包引用
通过 Chrome DevTools 的 Comparison 视图对比快照,发现 MemoryLeakService 的实例被一个全局缓存 Map 持有,而缓存的值是闭包函数,闭包又引用了完整的业务对象上下文,导致每次请求产生的临时对象都无法被回收。本质上是「缓存了不该缓存的东西」。
## 四、修复与验证
修复方案:缓存只保存必要字段,禁止保存闭包;同时为缓存增加 TTL 和最大容量限制,超出后按 LRU 淘汰。上线后连续观察一周,堆内存稳定在 350MB 左右,GC 恢复正常,问题彻底解决。
## 五、避坑指南
- 慎用全局变量和模块级缓存,持有大对象时要考虑生命周期;
- 定时器、事件监听器使用后必须清理,防止句柄堆积;
- 每次发版前跑一遍内存基线测试,用 --max-old-space-size 限制堆上限,配合 heap dump 对比工具形成常态化检查机制;
- 遇到内存问题优先看快照对比,别靠猜。