From 8e4de9b14500e195ec7f084c8c32b1782b31c5c9 Mon Sep 17 00:00:00 2001 From: Nitin Sahu Date: Sat, 21 Mar 2026 18:03:45 +0530 Subject: [PATCH] fix: null out effect.b in destroy_effect to prevent memory leak (#17980) ## Fix: #17881 ### Root Cause When a dynamic component switches (e.g. ``), branch effects are destroyed via `destroy_effect()`. However, the `effect.b` (Boundary reference) field was not cleared alongside other references (`next`, `prev`, `ctx`, `deps`, `fn`, `nodes`, `ac`). As a result, destroyed effects retained a reference to the `Boundary` instance, which holds references to component state and child effects. This prevented destroyed component subtrees from being garbage collected, causing memory usage to grow during repeated component switching. ### Solution Clear the boundary reference during effect destruction. ```diff effect.next = effect.prev = effect.teardown = effect.ctx = effect.deps = effect.fn = effect.nodes = effect.ac = + effect.b = null; ``` ### Test Plan * All existing tests pass: * runtime-runes: 2,459 * runtime-legacy: 3,294 * signals: 96 * Total: 5,849 tests * Added a dynamic component switching test that toggles components repeatedly and verifies correct DOM output. ### Validation Reproduced the issue using the example from #17881. After applying the fix: * Memory usage stabilizes during repeated component switching * Destroyed components are properly reclaimed by the garbage collector * No behavioral regressions observed ### Impact * Fixes memory leak in dynamic component switching * Minimal, safe change * No API or behavior changes --------- Co-authored-by: Rich Harris --- .changeset/fix-memory-leak-boundary.md | 5 +++++ packages/svelte/src/internal/client/reactivity/effects.js | 1 + 2 files changed, 6 insertions(+) create mode 100644 .changeset/fix-memory-leak-boundary.md diff --git a/.changeset/fix-memory-leak-boundary.md b/.changeset/fix-memory-leak-boundary.md new file mode 100644 index 0000000000..8a3b084981 --- /dev/null +++ b/.changeset/fix-memory-leak-boundary.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: null out `effect.b` in `destroy_effect` diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index aeffeedddd..54c8a17d79 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -559,6 +559,7 @@ export function destroy_effect(effect, remove_dom = true) { effect.fn = effect.nodes = effect.ac = + effect.b = null; }