Skip to content

fix: memory leak in MainThreadWorkspace#283450

Merged
roblourens merged 5 commits intomicrosoft:mainfrom
SimonSiefke:fix/memory-leak-main-thread-workspace
Mar 14, 2026
Merged

fix: memory leak in MainThreadWorkspace#283450
roblourens merged 5 commits intomicrosoft:mainfrom
SimonSiefke:fix/memory-leak-main-thread-workspace

Conversation

@SimonSiefke
Copy link
Contributor

@SimonSiefke SimonSiefke commented Dec 14, 2025

Fixes a memory leak in MainThreadWorkspace.

Details

When a provider is registered, it is both added to a Map called registeredEditSessionProviders as well as to this._toDispose.

this.registeredEditSessionProviders.set(handle, disposable);
this._toDispose.add(disposable);

However when a provider is unregistered, it is removed from the Map registeredEditSessionProviders, but not from this._toDispose

const disposable = this.registeredEditSessionProviders.get(handle);
disposable?.dispose();
this.registeredEditSessionProviders.delete(handle);

This causes the this._toDispose array to grow over time.

Change

The change is using a DisposableMap, ensuring this._toDispose doesn't grow when registering and unregistering providers.

private registeredCanonicalUriProviders = this._register(new DisposableMap<number, IDisposable>());

Before

When restarting extensions 37 times, the number functions in MainThreadWorkspace.$registerCanonicalUriProvider seems to grow by 3 each time. Entries 10/11:

extensions restart

After

When restarting extensions 37 times, the number functions in MainThreadWorkspace.$registerCanonicalUriProvider stays constant:

extensions restart

Copilot AI review requested due to automatic review settings March 13, 2026 09:35
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aims to stop MainThreadWorkspace from accumulating undisposed provider disposables across extension restarts by switching provider registration tracking to DisposableMap and relying on the Disposable base class for lifecycle management.

Changes:

  • Convert MainThreadWorkspace to extend Disposable and route disposal through super.dispose().
  • Replace provider Map<number, IDisposable> fields with DisposableMap<number, IDisposable> and use deleteAndDispose(...) on unregister.

You can also share your feedback on Copilot code review. Take the survey.

Copy link
Member

@roblourens roblourens left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@vs-code-engineering vs-code-engineering bot modified the milestone: 1.112.0 Mar 14, 2026
@roblourens roblourens merged commit dd8fdb7 into microsoft:main Mar 14, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants