帮助开发者审查代码中的内存泄漏与资源释放问题,定位常见生命周期隐患。
复制安装指令,让 AI 自动完成配置 · 推荐新手
请帮我安装 askskill 上的 "memory-leak-audit" 技能: 1. 下载 https://raw.githubusercontent.com/microsoft/vscode/main/.github/skills/memory-leak-audit/SKILL.md 2. 保存为 ~/.claude/skills/memory-leak-audit/SKILL.md 3. 装好后重载技能,告诉我可以用了
请审查这段前端代码是否存在事件监听未释放导致的内存泄漏,重点检查 addDisposableListener、DOM handlers 和组件销毁时机,并给出修复建议。
指出可能泄漏的监听器与未清理路径,并提供可落地的释放方式与修改建议。
分析这段包含生命周期回调的代码,检查 onWillDispose、Event.once、MutableDisposable 和 DisposableStore 的使用是否正确,找出潜在泄漏点。
返回逐项审计结果,说明哪些释放模式正确、哪些会造成对象残留或重复订阅。
下面是一份内存泄漏报告和相关代码,请帮我定位根因,判断是否与 disposable 管理不当有关,并给出最小改动的修复方案。
输出根因分析、受影响代码位置、修复思路,以及验证修复是否生效的检查建议。
The #1 bug category in VS Code. This skill encodes the patterns that prevent and fix leaks.
Work through each check in order. A single missed pattern can cause thousands of leaked objects.
Rule: Never use raw .onload, .onclick, or addEventListener() directly. Always use addDisposableListener().
// BAD — leaks a listener every call
this.iconElement.onload = () => { ... };
// GOOD — tracked and disposable
this._register(addDisposableListener(this.iconElement, 'load', () => { ... }));
Validated by: PR #280566 — Extension icon widget leaked 185 listeners after 37 toggles.
Rule: Use Event.once() for events that should only fire once (lifecycle events, close events, first-change events).
// BAD — listener stays registered forever after first fire
model.onDidDispose(() => store.dispose());
// GOOD — auto-removes after first invocation
Event.once(model.onDidDispose)(() => store.dispose());
Validated by: PRs #285657, #285661 — Terminal lifecycle hacks replaced with Event.once().
Rule: Objects created in methods called multiple times must NOT be registered to the class this._register(). Use MutableDisposable or return IDisposable to the caller.
// BAD — every call adds another listener to the class store
startSearch() {
this._register(this.model.onResults(() => { ... }));
}
// GOOD — MutableDisposable ensures max 1 listener
private readonly _searchListener = this._register(new MutableDisposable());
startSearch() {
this._searchListener.value = this.model.onResults(() => { ... });
}
When the event should only fire once per method call, combine Event.once() with MutableDisposable — this auto-removes the listener after the first invocation while still guarding against repeated calls:
private readonly _searchListener = this._register(new MutableDisposable());
startSearch() {
this._searchListener.value = Event.once(this.model.onResults)(() => { ... });
}
Validated by: PR #283466 — Terminal find widget leaked 1 listener per search.
Rule: When creating a DisposableStore tied to a model's lifetime, register model.onWillDispose(() => store.dispose()) to the store itself.
const store = new DisposableStore();
store.add(model.onWillDispose(() => store.dispose()));
store.add(model.onDidChange(() => { ... }));
Validated by: Pattern used in chatEditingSession.ts, fileBasedRecommendations.ts, testingContentProvider.ts.
Rule: When using factory methods that create pooled objects (lists, trees), disposables must be registered to the individual item, not the pool class.
// BAD — registers to pool, never cleaned per item
createItem() {
const item = new Item();
this._register(item.onEvent(() => { ... }));
return item;
}
// GOOD — wrap with item-scoped disposal
createItem(): IDisposable & Item {
const store = new DisposableStore();
const item = new Item();
store.add(item.onEvent(() => { ... }));
return { ...item, dispose: () => store.dispose() };
}
Validated by: PR #290505 — Chat content parts CollapsibleListPool and TreePool leaked disposables.
Rule: Every test suite that creates disposable objects must call ensureNoDisposablesAreLeakedInTestSuite().
…
帮助开发者验证 Azure DevOps 流水线改动,快速排查构建与 YAML 配置问题。
用于审计 ECC Tools 的成本与计费异常,快速定位滥用、泄漏与重复任务来源。