必须手动用 node --inspect=9229 app.js 启动再 VSCode attach,才能捕获 require 阶段闭包、全局缓存等早期泄漏源;需在 chrome://inspect 中连接并用 Memory 面板对比至少 3 次堆快照,重点分析 Retained Size 和 Detached 类型对象。

直接 attach 调试才能捕获真实泄漏起点
VSCode 自带的 launch 配置启动 Node.js 进程时,调试器介入太晚,require 阶段创建的闭包、全局缓存、未取消的定时器等早期泄漏源根本来不及捕获。必须改用手动 node --inspect=9229 app.js 启动,再让 VSCode attach 连接——这样进程生命周期完全可控,GC 前后的堆状态才可比对。
关键点:
-
launch.json中 type 必须设为attach,port 与命令行一致(如9229) -
localRoot和remoteRoot都填"${workspaceFolder}",否则源码映射失败,快照里看不到变量名 - 避免在生产环境跑
--inspect,开发环境建议加--max-old-space-size=1024加速 OOM 触发,缩短定位周期
Chrome DevTools Memory 面板才是唯一可靠分析入口
VSCode 内置调试器不支持完整的堆快照对比功能。必须打开 chrome://inspect → Configure → 添加 localhost:9229 → 刷新后点击 inspect,进入 Memory 面板操作。
实操建议:
使用约定式提交(Conventional Commits)生成标准化的 Git 提交信息。适用于以下场景: (1) 暂存更改时需要提交信息; (2) 审查 diff 时希望获得结构化摘要; (3) 编写 PR 描述或变更日志(changelog)条目; (4) 用户明确要求“为本次更改编写提交信息”。 该功能会分析已暂存/未暂存的 diff,并生成按类型(type)划分范围的提交信息。
- 不要只拍一次快照。至少拍 3 次:空载时(snapshot-0)、触发可疑行为后(如调用一次接口,snapshot-1)、等待 30 秒 GC 后(snapshot-2)
- 右键快照 →
Compare to previous snapshot,按Retained Size降序排列 - 重点筛
Closure类型,展开看其Retainers面板——若引用链里出现cache、handlers、pendingRequests或大对象(如Buffer、EventEmitter),基本就是泄漏根因
Detached 对象不是 DOM 专属,Node.js 里也大量存在
Chrome DevTools 把“长期存活但无引用路径”的对象统一标为 Detached,Node.js 场景下常见于:setInterval 回调捕获外部作用域、未清理的 EventEmitter.on 监听器、闭包持有 fs.readFileSync 返回的大 Buffer。
典型陷阱:
- 箭头函数 +
setInterval组合未配clearInterval,整个外层作用域无法释放 -
server.on('request', handler)在循环中反复绑定,但没调用server.removeAllListeners() - 模块顶层定义的 Map 缓存,key 是请求对象或临时 buffer,没做弱引用或 TTL 清理
扩展宿主(Extension Host)和渲染进程快照不能混用
VSCode 的内存泄漏可能发生在三个不同进程:渲染器(UI)、扩展宿主(插件)、Node.js 子进程(LSP / 调试目标)。你正在分析的是 Node.js 应用本身,那就要确保 Chrome DevTools 连接的是 node --inspect 进程,而不是 code --inspect-renderer 或 Extension Host 进程。
验证方式:
- 在
chrome://inspect页面,目标名称应为file:///path/to/app.js或类似 Node 入口路径,而非vscode-file://… - 快照中
Constructor列若大量出现NativeModule、Module、Server等 Node 原生类,说明连接正确 - 若看到大量
HTMLDivElement或Webview相关构造函数,说明误连了渲染进程,需重新检查端口和启动方式
setTimeout 回调里无意捕获了整个 request 对象,而这个对象又引用了 body buffer —— 它不会报错,也不会立刻崩,但每秒累积几 MB,两小时后就卡死。别信日志,别信内存数字,只信两次快照之间那个突兀增长的 Retained Size。

















