应手动用 node --inspect=9229 app.js 启动进程,再通过 VSCode attach 调试,配合 Chrome DevTools 的 Memory 面板抓取并对比堆快照,重点分析 Retained Size 和 Detached 类型对象,结合 process.memoryUsage() 与 --trace-gc 日志验证泄漏。

直接用 VSCode 启动带 --inspect 的 Node 进程
VSCode 调试 Node.js 的核心前提是让目标进程暴露调试端口。不能只靠 launch.json 里配 program 然后点运行——那样无法捕获启动前的内存分配,也难以复现某些泄漏场景。
更可靠的做法是手动加 --inspect 启动,再让 VSCode attach:
- 终端执行:
node --inspect=9229 app.js(端口可换,但需和后续配置一致) - VSCode 中创建
.vscode/launch.json,类型设为attach,指定port和address(默认"localhost") - 确保
localRoot和remoteRoot一致(本地开发时通常都填"${workspaceFolder}")
这样做的好处是:进程生命周期完全可控,能第一时间捕获 require 阶段的闭包引用、全局缓存误存等早期泄漏源。
用 Chrome DevTools 打开 memory 面板抓堆快照
VSCode 自带的调试器不支持完整的内存分析功能。必须通过 chrome://inspect 连接到 --inspect 端口,再进 Memory 面板操作。
关键操作步骤:
- 在 Chrome 地址栏输入
chrome://inspect→ 点击Configure...→ 加入localhost:9229 - 刷新后看到你的 Node 进程 → 点
inspect - 切到
Memory标签 → 选Heap snapshot→ 点Capture heap snapshot - 重复操作 2–3 次(比如请求接口前后、定时器触发后),生成多个快照用于对比
注意:快照体积大,不要在生产环境直接做;开发环境建议用 --max-old-space-size=1024 限制堆大小,加速 OOM 触发和定位。
对比快照时重点看 Retained Size 和 Detached DOM 类型
Node.js 里没有 DOM,但 Chrome DevTools 仍会把某些长期存活却无引用路径的对象标为 Detached * (比如被闭包捕获后又没释放的 Buffer、EventEmitter 实例)。这类对象的 Retained Size 往往异常高,是泄漏强信号。
筛选技巧:
- 在快照列表里右键 →
Compare to previous snapshot - 排序列选
Retained Size,从大到小扫 - 展开可疑构造函数(如
Array、Object、Buffer、Timeout),看Retainers面板里谁在 hold 它 - 特别留意
Closure下的变量名,比如cache、handlers、pendingRequests—— 这些常是泄漏源头
一个典型陷阱:setInterval 回调里用了箭头函数并捕获了外部大对象,但忘记 clearInterval,导致整个作用域链无法 GC。
配合 process.memoryUsage() 和 --trace-gc 快速验证泄漏模式
堆快照太重,日常监控建议用轻量方式交叉验证:
- 在代码里加日志:
console.log(process.memoryUsage()),重点关注heapUsed和heapTotal的增长趋势 - 启动时加
--trace-gc --trace-gc-verbose,观察 GC 日志是否出现scavenge频繁但mark-sweep越来越少 → 典型老生代堆积 - 用
node --expose-gc app.js,然后在调试器里手动触发global.gc()(仅限开发),看内存是否回落
如果 gc() 后 heapUsed 依然持续上涨,基本可以确定存在强引用泄漏,这时候再回过去查快照才高效。
真正难的不是抓快照,而是区分「正常缓存增长」和「失控引用」——得结合业务逻辑反复比对 retainers 链,尤其是第三方库内部的事件监听器、定时器、Promise 链残留。








