Skip to content

Memory leak in gettingStarted #216858

@SimonSiefke

Description

@SimonSiefke

Does this issue occur when all extensions are disabled?: Yes

  • VS Code Version: 1.90.2
  • OS Version: Ubuntu 24.04

Steps to Reproduce:

  1. Open the welcome fundamentals view
  2. Expand "Rich support for all your languages"
  3. Expand "Choose you theme"
  4. Repeat expanding either element several times
  5. Notice that the number of webview event listeners grows each time
{
  "eventListenersWithStackTrace": [
    {
      "type": "drop",
      "description": "()=>{this.mb()}",
      "objectId": "-6222731808989745711.4.8035",
      "stack": [
        "listener (/resources/app/out/vs/workbench/workbench.desktop.main.js:1264:24774)",
        "new u (resources/app/out/vs/workbench/workbench.desktop.main.js:138:30819)",
        "r (resources/app/out/vs/workbench/workbench.desktop.main.js:138:30983)",
        "o.mountTo (resources/app/out/vs/workbench/workbench.desktop.main.js:1265:24770)",
        "ve.hc (resources/app/out/vs/workbench/workbench.desktop.main.js:2386:68224)",
        "ve.ic (resources/app/out/vs/workbench/workbench.desktop.main.js:2386:72222)",
        "ve.Yb (resources/app/out/vs/workbench/workbench.desktop.main.js:2386:66403)",
        "HTMLButtonElement.<anonymous> (resources/app/out/vs/workbench/workbench.desktop.main.js:2386:65333)"
      ],
      "count": 291,
      "originalStack": ["/src/vs/workbench/contrib/webview/browser/webviewElement.ts:469:60"],
      "originalName": null
    },
    {
      "type": "drop",
      "description": "()=>{this.mb()}",
      "objectId": "-6222731808989745711.4.8037",
      "stack": [
        "listener (/resources/app/out/vs/workbench/workbench.desktop.main.js:1264:24774)",
        "new u (resources/app/out/vs/workbench/workbench.desktop.main.js:138:30819)",
        "r (resources/app/out/vs/workbench/workbench.desktop.main.js:138:30983)",
        "o.mountTo (resources/app/out/vs/workbench/workbench.desktop.main.js:1265:24770)",
        "ve.hc (resources/app/out/vs/workbench/workbench.desktop.main.js:2386:68448)",
        "ve.ic (resources/app/out/vs/workbench/workbench.desktop.main.js:2386:72222)",
        "ve.Yb (resources/app/out/vs/workbench/workbench.desktop.main.js:2386:66403)",
        "HTMLButtonElement.<anonymous> (resources/app/out/vs/workbench/workbench.desktop.main.js:2386:65333)"
      ],
      "count": 291,
      "originalStack": ["/src/vs/workbench/contrib/webview/browser/webviewElement.ts:469:60"],
      "originalName": null
    },
    {
      "type": "dragend",
      "description": "()=>{this.mb()}",
      "objectId": "-6222731808989745711.4.10635",
      "stack": ["listener (/resources/app/out/vs/workbench/workbench.desktop.main.js:1264:24846)"],
      "count": 194,
      "originalStack": ["/src/vs/workbench/contrib/webview/browser/webviewElement.ts:475:66"],
      "originalName": null
    },
    {
      "type": "message",
      "description": "B=>{if(!(!this.g||B?.data?.target!==this.a)){if(B.origin!==this.ob(this.g)){console.log(`Skipped renderer receiving message due to mismatched origins: ${B.origin} ${this.ob}`);return}if(B.data.channel===\"webview-ready\"){if(this.H)return;this.Q.debug(`Webview(${this.a}): webview ready`),this.H=B.ports[0],this.H.onmessage=W=>{const F=this.I.get(W.data.channel);if(!F){console.log(`No handlers found for '${W.data.channel}'`);return}F?.forEach(j=>j(W.data.data,W))},this.n?.classList.add(\"ready\"),this.s.type===P.Type.Initializing&&this.s.pendingMessages.forEach(({channel:W,data:F,resolve:j})=>j(this.pb(W,F))),this.s=P.Ready,z.dispose()}}}",
      "objectId": "-6222731808989745711.4.10217",
      "stack": ["listener (/resources/app/out/vs/workbench/workbench.desktop.main.js:1264:24942)"],
      "count": 193,
      "originalStack": ["/src/vs/workbench/contrib/webview/browser/webviewElement.ts:486:86"],
      "originalName": "e"
    },
    {
      "type": "dragend",
      "description": "()=>{this.mb()}",
      "objectId": "-6222731808989745711.4.8433",
      "stack": [
        "listener (/resources/app/out/vs/workbench/workbench.desktop.main.js:1264:24846)",
        "new u (resources/app/out/vs/workbench/workbench.desktop.main.js:138:30819)",
        "r (resources/app/out/vs/workbench/workbench.desktop.main.js:138:30983)",
        "o.mountTo (resources/app/out/vs/workbench/workbench.desktop.main.js:1265:24829)",
        "ve.hc (resources/app/out/vs/workbench/workbench.desktop.main.js:2386:68224)",
        "ve.ic (resources/app/out/vs/workbench/workbench.desktop.main.js:2386:72222)",
        "ve.Yb (resources/app/out/vs/workbench/workbench.desktop.main.js:2386:66403)",
        "HTMLButtonElement.<anonymous> (resources/app/out/vs/workbench/workbench.desktop.main.js:2386:65333)"
      ],
      "count": 97,
      "originalStack": ["/src/vs/workbench/contrib/webview/browser/webviewElement.ts:475:66"],
      "originalName": null
    },
    {
      "type": "dragend",
      "description": "()=>{this.mb()}",
      "objectId": "-6222731808989745711.4.8435",
      "stack": [
        "listener (/resources/app/out/vs/workbench/workbench.desktop.main.js:1264:24846)",
        "new u (resources/app/out/vs/workbench/workbench.desktop.main.js:138:30819)",
        "r (resources/app/out/vs/workbench/workbench.desktop.main.js:138:30983)",
        "o.mountTo (resources/app/out/vs/workbench/workbench.desktop.main.js:1265:24829)",
        "ve.hc (resources/app/out/vs/workbench/workbench.desktop.main.js:2386:68448)",
        "ve.ic (resources/app/out/vs/workbench/workbench.desktop.main.js:2386:72222)",
        "ve.Yb (resources/app/out/vs/workbench/workbench.desktop.main.js:2386:66403)",
        "HTMLButtonElement.<anonymous> (resources/app/out/vs/workbench/workbench.desktop.main.js:2386:65333)"
      ],
      "count": 97,
      "originalStack": ["/src/vs/workbench/contrib/webview/browser/webviewElement.ts:475:66"],
      "originalName": null
    }
  ],
  "isLeak": true
}

Test script

git clone [email protected]:SimonSiefke/vscode-memory-leak-finder.git &&
cd vscode-memory-leak-finder &&
git checkout v5.47.0 &&
npm ci &&
node packages/cli/bin/test.js --cwd packages/e2e --only welcome-page.edit --measure event-listeners-with-stack-trace --runs 97 --measure-after --check-leaks &&
cat .vscode-memory-leak-finder-results/event-listeners-with-stack-trace/welcome-page.edit.json

Additional Information

It seems, every time when expanding a section in the gettingStartedView, a new webview is created:

// gettingStarted.ts
private async buildMediaComponent(stepId: string, forceRebuild: boolean = false) {
		if (stepToExpand.media.type === 'svg') {
			this.webview = this.mediaDisposables.add(this.webviewService.createWebviewElement({ title: undefined, options: { disableServiceWorker: true }, contentOptions: {}, extension: undefined }));
			this.webview.mountTo(this.stepMediaComponent, this.window);
		} else if (stepToExpand.media.type === 'markdown') {
			this.webview = this.mediaDisposables.add(this.webviewService.createWebviewElement({ options: {}, contentOptions: { localResourceRoots: [stepToExpand.media.root], allowScripts: true }, title: '', extension: undefined }));
			this.webview.mountTo(this.stepMediaComponent, this.window);
		}
	}
}

Possible solution

A possible solution could be to to dispose the mediaDisposables disposables before creating another webview:

// gettingStarted.ts
private async buildMediaComponent(stepId: string, forceRebuild: boolean = false) {
        this.mediaDisposables.clear(); // clean up old webview disposables
		if (stepToExpand.media.type === 'svg') {
			this.webview = this.mediaDisposables.add(this.webviewService.createWebviewElement({ title: undefined, options: { disableServiceWorker: true }, contentOptions: {}, extension: undefined }));
			this.webview.mountTo(this.stepMediaComponent, this.window);
		} else if (stepToExpand.media.type === 'markdown') {
			this.webview = this.mediaDisposables.add(this.webviewService.createWebviewElement({ options: {}, contentOptions: { localResourceRoots: [stepToExpand.media.root], allowScripts: true }, title: '', extension: undefined }));
			this.webview.mountTo(this.stepMediaComponent, this.window);
		}
	}
}

Metadata

Metadata

Assignees

Labels

bugIssue identified by VS Code Team member as probable bugfreeze-slow-crash-leakVS Code crashing, performance, freeze and memory leak issuesgetting-startedinsiders-releasedPatch has been released in VS Code Insiderspostponed-during-endgameEndgame champ removed the planed milestoneverifiedVerification succeeded

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions