LearnNewsExamplesServices
Frontmatter
id8961
titleFix: Memory Leak in Neo.component.Canvas due to missing destroy()
stateClosed
labels
bugai
assigneestobiu
createdAtFeb 3, 2026, 2:01 PM
updatedAtFeb 3, 2026, 2:12 PM
githubUrlhttps://github.com/neomjs/neo/issues/8961
authortobiu
commentsCount1
parentIssue8930
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtFeb 3, 2026, 2:12 PM

Fix: Memory Leak in Neo.component.Canvas due to missing destroy()

Closed v12.0.0 bugai
tobiu
tobiu commented on Feb 3, 2026, 2:01 PM

Neo.component.Canvas is missing a destroy() method. While it correctly registers the canvas with the worker on mount, it fails to unregister it on destruction.

Consequence: The Canvas Worker retains OffscreenCanvas contexts, render loops, and data structures indefinitely, leading to a memory leak and potential GPU process crash over time as views are created and destroyed.

Task:

  1. Implement destroy() in src/component/Canvas.mjs.
  2. Send a destroyCanvas (or similar unregister) message to the Canvas Worker.
  3. Implement the corresponding destroyCanvas handler in src/worker/Canvas.mjs (and specifically in src/canvas/Sparkline.mjs if it maintains its own map) to clean up the internal state and stop render loops.
tobiu added the bug label on Feb 3, 2026, 2:01 PM
tobiu added the ai label on Feb 3, 2026, 2:01 PM
tobiu added parent issue #8930 on Feb 3, 2026, 2:01 PM
tobiu referenced in commit 05a855c - "Fix: Memory Leak in Neo.component.Canvas due to missing destroy() (#8961)" on Feb 3, 2026, 2:10 PM
tobiu
tobiu Feb 3, 2026, 2:10 PM

Input from Gemini 3 Pro:

✦ I have fixed the memory leak in Neo.component.Canvas and Neo.component.Sparkline.

Changes:

  1. src/worker/Canvas.mjs: Added unregisterCanvas to remove canvas references from the worker's internal map.
  2. src/component/Canvas.mjs:
    • Implemented destroy() to call unregisterCanvas if the canvas was registered.
    • Updated afterSetMounted() to call unregisterCanvas when the component is unmounted (value === false).
  3. src/canvas/Sparkline.mjs:
    • Added unregister remote method to cleanup the renderer's specific state (active render loops, item maps).
  4. src/component/Sparkline.mjs:
    • Implemented destroy() to call renderer.unregister.
    • Updated afterSetMounted() to call renderer.unregister on unmount.

Outcome: OffscreenCanvas contexts and worker-side render loops are now properly cleaned up when a component is destroyed or unmounted (e.g., via removeDom: true). This prevents memory leaks and ensures long-term stability.

tobiu assigned to @tobiu on Feb 3, 2026, 2:11 PM
tobiu closed this issue on Feb 3, 2026, 2:12 PM