跳到主要内容

订阅列表为什么要收集可释放对象,漏了会怎样?

结论​

context.subscriptions 是插件的释放清单。推进去的对象实现了 Disposable,插件停用时宿主会逐个调用 dispose。漏掉的监听、定时器、诊断集合、Webview 会留在扩展宿主里,重载后还可能叠成多份。

展开​

注册类 API 几乎都返回 Disposable:

context.subscriptions.push(
vscode.commands.registerCommand("myext.hello", () => {
/* ... */
}),
vscode.workspace.onDidChangeTextDocument(onChange),
diagnosticCollection,
statusBarItem
);

停用时宿主遍历这个数组。命令被卸掉,事件不再回调,诊断波浪线被清掉,状态栏项消失。

自己创建的资源也要包一层:

const timer = setInterval(poll, 5000);
context.subscriptions.push({ dispose: () => clearInterval(timer) });

漏收集的典型后果:

  • onDidChangeTextDocument 每重载一次多挂一个监听,补全和诊断被重复计算。
  • createDiagnosticCollection 没释放,旧诊断清不掉。
  • setInterval 继续打请求。
  • Webview 面板关不干净,消息回调打到已销毁对象上。

deactivate 里再手动 dispose 一遍通常没必要,订阅列表已经做了。deactivate 留给那些没有放进订阅、又必须按顺序停下的进程,比如语言服务器要先发 shutdown。

容易讲错的点​

  • subscriptions 不是响应式状态库,不要拿它存业务数据。
  • 只 push 还不够:如果回调里又 createWebviewPanel,新面板也要在关闭时释放,或自己放进订阅。
  • 事件回调抛错不会自动取消订阅,宿主仍认为插件活着。