Skip to content

Release the application references on destroy - #9190

Merged
mvaligursky merged 1 commit into
mainfrom
mv-app-global-destroy
Aug 18, 2026
Merged

Release the application references on destroy#9190
mvaligursky merged 1 commit into
mainfrom
mv-app-global-destroy

Conversation

@mvaligursky

Copy link
Copy Markdown
Contributor

Found while investigating #3886, and the dominant reason a destroyed application leaks.

app-base.js has a module scoped app reference, exported publicly as pc.app. It is assigned in the AppBase constructor and again on every frame update, but AppBase#destroy never clears it - unlike the equivalent globals right next to it (setApplication(null), script.app = null, AppBase._applications[canvasId] = null).

A destroyed application is therefore retained forever, unless a new application happens to overwrite the reference. Everything still referenced by the destroyed instance goes with it - most notably assets (the full AssetRegistry, every Asset, and their data and file.contents payloads), plus xr, the default layers, the frame graph and the bound tick closure.

Verified with a WeakRef probe on the null device: before this change, a destroyed application is uncollectable even when the test holds no reference to it at all, and becomes collectable only once a second application is created; after it, the destroyed application and its registry are collected right away.

Changes

  • AppBase#destroy clears the module scoped reference, guarded by app === this so that destroying an old application does not clear the pointer to a newer one (mirroring the existing getApplication() === this check).
  • ResourceLoader#destroy clears its _app reference, so a handler or loader which outlives the application does not keep it alive. Both of its use sites are safe: load early-returns once the handlers have been cleared, and the variant lookup in Asset#file already falls back to getApplication().
  • Test asserting that pc.app, getApplication() and loader._app are all cleared by destroy.

Related but independent: #9189 clears the Asset to AssetRegistry back-reference, which is the other half of #3886.

🤖 Generated with Claude Code

The module scoped application reference in app-base.js, exported as pc.app, was
assigned in the constructor and on every frame update, but never cleared by
AppBase#destroy - unlike the equivalent globals (getApplication, script.app and
AppBase._applications). A destroyed application was therefore kept alive
indefinitely, along with everything still referenced by it, such as its asset
registry and all of its assets, unless a new application happened to replace it.

ResourceLoader#destroy now clears its application reference as well, so that a
handler or loader which outlives the application does not keep it alive.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Build size report

This PR changes the size of the minified bundles.

Bundle Minified Gzip Brotli
playcanvas.min.js 2367.8 KB (+0.0 KB, +0.00%) 608.1 KB (+0.0 KB, +0.00%) 472.0 KB (−0.5 KB, −0.10%)
playcanvas.min.mjs 2365.2 KB (+0.0 KB, +0.00%) 607.1 KB (−0.0 KB, −0.00%) 471.4 KB (−0.2 KB, −0.04%)

@mvaligursky mvaligursky left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Automated PR review by Codex (GPT-5).

Reviewed single- and multi-application teardown behavior, live ES-module binding semantics for pc.app, ResourceLoader request/cache behavior after destroy, retained-loader references, API compatibility, and performance impact. No blocking issues found.

The identity guard correctly preserves a newer active application when an older one is destroyed, while releasing the destroyed loader reference. The focused Application suite passes (3/3), an additional two-application teardown probe passes, and all repository CI checks are green. Because this PR is authored by the account posting the review, GitHub does not permit a formal self-approval; this COMMENTED review records the clean automated result.

@mvaligursky
mvaligursky merged commit 2da8389 into main Aug 18, 2026
10 checks passed
@mvaligursky
mvaligursky deleted the mv-app-global-destroy branch August 18, 2026 08:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant