M

memory-leak-audit

by @microsoftv
4.4(20)

memory-leak-audit、AIエージェントスキル、作業効率と自動化能力を向上。

ai-engineeringprompt-engineeringautomationai-agentsGitHub
インストール方法
npx skills add https://github.com/microsoft/vscode --skill memory-leak-audit
compare_arrows

Before / After 効果比較

1 组
使用前

アプリケーションが数時間稼働するとメモリ使用量が継続的に増加します。手動でのトラブルシューティングでは、イベントリスナーやDOM参照を一つずつ確認する必要があり、特定が困難で見落としやすいです。

使用後

AIが一般的なメモリリークパターンを自動で識別し、未クリーンアップのリスナーやDOM参照を正確に特定し、具体的な修復策を提供します。予防的なチェックにより新たな問題を回避します。

SKILL.md

memory-leak-audit

Memory Leak Audit

The #1 bug category in VS Code. This skill encodes the patterns that prevent and fix leaks.

When to Use

  • Reviewing code that registers event listeners or DOM handlers

  • Fixing reported memory leaks (listener counts growing over time)

  • Creating objects in methods that are called repeatedly

  • Working with model lifecycle events (onWillDispose, onDidClose)

  • Adding event subscriptions in constructors or setup methods

Audit Checklist

Work through each check in order. A single missed pattern can cause thousands of leaked objects.

Step 1: DOM Event Listeners

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.

Step 2: One-Time Events

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().

Step 3: Repeated Method Calls

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.

Step 4: Model-Tied DisposableStores

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.

Step 5: Resource Pool Patterns

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.

Step 6: Test Validation

Rule: Every test suite that creates disposable objects must call ensureNoDisposablesAreLeakedInTestSuite().

import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';

suite('MyFeature', () => {
    ensureNoDisposablesAreLeakedInTestSuite();

    test('does something', () => {
        // test disposables are tracked automatically
    });
});

Quick Reference

Scenario Pattern Anti-Pattern

DOM events addDisposableListener() .onclick =, addEventListener()

One-time events Event.once(event)(handler) event(handler) for lifecycle

Repeated methods MutableDisposable or return IDisposable this._register() in non-constructor

Model lifecycle store.add(model.onWillDispose(...)) Forgetting cleanup

Pooled objects Item-scoped DisposableStore Pool-scoped this._register()

Tests ensureNoDisposablesAreLeakedInTestSuite() No leak checking

Verification

After fixing leaks, verify by:

  • Checking listener counts before/after repeated operations

  • Running ensureNoDisposablesAreLeakedInTestSuite() in tests

  • Confirming object counts stabilize (don't grow linearly with usage)

Weekly Installs431Repositorymicrosoft/vscodeGitHub Stars183.5KFirst SeenFeb 24, 2026Security AuditsGen Agent Trust HubPassSocketPassSnykPassInstalled oncursor431amp430codex430gemini-cli430opencode430github-copilot430

ユーザーレビュー (0)

レビューを書く

効果
使いやすさ
ドキュメント
互換性

レビューなし

統計データ

インストール数3.2K
評価4.4 / 5.0
バージョン
更新日2026年10月3日
比較事例1 件

ユーザー評価

4.4(20)
5
60%
4
40%
3
0%
2
0%
1
0%

この Skill を評価

0.0

対応プラットフォーム

🔧Claude Code

タイムライン

作成2026年4月9日
最終更新2026年10月3日
🎁 Agent 知識カード
アンケート