必须用 node --inspect 启动,因 vscode 默认 launch.json 调试介入太晚,无法捕获 require 阶段闭包、模块缓存、未清除定时器等早期泄漏源;真实泄漏多始于进程初始化,需终端执行 node --inspect=9229 --max-old-space-size=1024 --expose-gc app.js,配合 chrome devtools memory 面板拍至少3次堆快照并对比 retained size 定位根因。

必须用 node --inspect 启动,不能靠 VSCode 自带 launch
VSCode 的 launch.json 直接启动 Node 进程时,调试器介入太晚,require 阶段创建的闭包、模块缓存、未清除的定时器等早期泄漏源根本捕获不到。真实泄漏往往发生在进程初始化阶段,而 VSCode 默认配置只在代码执行后才挂载调试器。
实操建议:
- 终端手动执行:
node --inspect=9229 --max-old-space-size=1024 --expose-gc app.js(加--expose-gc是为了后续手动触发 GC) -
launch.json类型必须设为attach,port与命令行一致(如9229),localRoot和remoteRoot都填"${workspaceFolder}",否则快照里看不到变量名 - 避免在生产环境启用
--inspect;开发环境加--max-old-space-size=1024可加速 OOM 触发,缩短定位周期
Chrome DevTools Memory 面板才是唯一可靠入口
VSCode 内置调试器不支持堆快照对比功能,process.memoryUsage() 返回的 heapUsed 和 external 只是粗略统计,比如一个 150MB 的 Buffer 算在 external 里,但你根本看不出它属于哪个变量。
实操路径:
微软正式发布 Visual Studio Code 1.118 版本 。本次更新重点强化了 AI 开发体验与企业管理能力,其中最引人注目的是新增 Copilot CLI 远程控制功能,允许开发者通过手机或网页远程监控和接管 AI 会话 。同时,为了提高 AI 的运行性价比,新版本优化了令牌缓存策略以降低成本 。此外,1.118 版还引入了 Chronicle 本地历史追踪、TypeScript 7.0 支持以及更严格的企业级访问管控 。
- 浏览器访问
chrome://inspect→ Configure → 添加localhost:9229→ 刷新后点击inspect - 切到
Memory面板 → 点Take heap snapshot - 至少拍 3 次:空载时(
snapshot-0)、触发可疑行为后(如调用一次接口,snapshot-1)、等待 30 秒 GC 后(snapshot-2)
对比快照时盯 Retained Size,不是 Shallow Size
Shallow Size 只是对象自身占用,Retained Size 才代表“它活着,就拖着一堆东西活不了”——这才是泄漏的真实代价。Node.js 场景下,Detached 不是 DOM 专属,被闭包长期捕获却没释放的 Buffer、EventEmitter 实例、setInterval 回调都会被标为 Detached。
筛选重点:
- 右键快照 →
Compare to previous snapshot - 按
Retained Size降序排列,优先看Closure、ArrayBuffer、String、Map - 展开可疑
Closure→ 看Retainers面板 → 若引用链出现cache、handlers、pendingRequests或大对象(如Buffer),基本就是根因 - 典型陷阱:
setInterval(() => { ... }, 1000)忘记clearInterval;server.on('request', handler)在循环中反复绑定却没调server.removeAllListeners()
别把 Extension Host 和你的 Node 进程搞混
Developer: Open Process Explorer 查的是 VSCode 插件(extensionHost)内存,和你在终端跑的 node app.js 完全无关。混淆这两者是“查半天没效果”的最常见原因。
确认你真在分析自己的服务:
- 终端执行
ps aux | grep node,找到你手动启动的进程 PID - 在
chrome://inspect里看到的进程名应为app.js或你指定的文件名,不是Extension Host - 如果发现内存飙升的是
extensionHost,那是插件问题,该用Developer: Open Webview Developer Tools去抓它的快照,而不是折腾你的后端代码
Map 缓存,key 是临时 Buffer,没做 TTL 清理;或者 sharp 实例没调 .destroy(),底层 VIPS 资源一直挂着。这些都得靠 Retainers 面板一层层点进去才能看见。










