
用 Chrome DevTools Memory 面板抓快照,别在 VSCode 调试器里点“捕获堆快照”
VSCode 内置调试器的“Heap Snapshot”按钮根本不会生成可比对的快照,点完只显示空列表或报 Not available in this context。必须绕过它,走 Chrome DevTools 协议链。
实操建议:
- 终端执行
code --inspect-extensions=9333启动 VSCode(不是node --inspect,那是跑你自己的 Node 服务) - 浏览器打开
chrome://inspect→ Configure → 添加localhost:9333→ 刷新后找到 “VS Code Extension Host” → 点inspect - 进 Memory 面板 → 选
Heap snapshot→ 连续拍三次:snapshot-0(空载)、snapshot-1(触发可疑操作后)、snapshot-2(等 30 秒 GC 后) - 右键
snapshot-2→Compare to snapshot-1,按Retained Size降序排,重点筛Closure和Detached类型
断点设在 dispose()、deactivate() 或事件解绑前,才能卡住泄漏源头
内存泄漏往往发生在资源该释放却没释放的瞬间,比如 webview.dispose() 没调、emitter.off('data', handler) 漏写、或 clearInterval(id) 被跳过。VSCode 断点必须打在这些函数体第一行,而不是等内存涨上去再查。
实操建议:
- 在插件源码中定位
deactivate()函数(如 GitLens 的src/extension.ts),在context.subscriptions.forEach(...)前加断点 - 若用
EventEmitter,在.on()调用处打条件断点:event === 'message' && listener.name === 'handleMessage',确认是否重复注册 - 检查
setTimeout/setInterval是否配对clearTimeout/clearInterval;断点设在定时器回调入口,看this或闭包变量是否意外持有了大对象 - 避免在
activate()里直接定义箭头函数监听器——它会隐式捕获整个this上下文,改用显式绑定或普通函数 + 手动清理
对比快照时盯紧 Retainers 链,别被 Shallow Size 带偏
Shallow Size 是对象自身占多少字节,Retained Size 才是它拖着不放的所有下游对象总和。一个 Closure 自身可能只有几百字节,但 Retained Size 达 4.3MB,说明它锁死了整个缓存树。
实操建议:
- 展开可疑
Closure后,立刻看右侧Retainers面板:若出现this._cache、context.subscriptions、module.exports,直接点进去跳转源码 - 若
Retainers里有pendingRequests或handlers,基本可判定是事件监听未解绑或请求未 abort - 注意区分框架代码和你自己的代码:
Retainers链末端如果是vscode-gitlens/src/git/gitService.ts这类路径,就是真实泄漏点;如果是electron/js2c/renderer_init.js,大概率是上层逻辑没清理干净 - 遇到
Detached HTMLDivElement,别只盯着 DOM 树——它背后往往是 Webview 实例没调dispose(),根源还在 JS 层的生命周期管理
禁用扩展 ≠ 终止进程,不重启窗口等于白忙
在 VSCode 设置里把 GitLens 设为“禁用”,只是告诉下次启动时别加载它,当前 Extension Host 进程仍带着全部已分配内存继续跑。你看到的内存数字纹丝不动,不是插件没泄漏,是你没真正把它踢出去。
实操建议:
- 禁用可疑扩展后,必须关闭当前 VSCode 窗口 → 全部退出 → 重新打开,强制拉起新 Extension Host 进程
- 验证是否真解决:打开相同文件 → 等 2 分钟 → 查看
Developer: Open Process Explorer中 Extension Host 的 Memory 值是否稳定在 100MB 以下 - 若仍高,用
code --disable-extensions启动纯净版 VSCode 对比——如果此时内存正常,问题 100% 出在扩展;如果依然上涨,得查渲染进程或用户设置里的全局监听器 - 别信“Reload Window”:它只重载渲染层,Extension Host 进程不重启,泄漏对象全还在堆里
最易被忽略的一点:所有快照对比动作必须在同一个 Chrome DevTools 实例里完成。跨窗口打开多个 inspect 页面,会导致快照元数据丢失、Retainers 链断裂、甚至 Compare 功能失效。











