Skip to content

[Bug] Memory Leak: Deck.finalize() drops CanvasContext reference instead of destroying it, leaving ResizeObserver attached #10355

Description

@LeLunZ

Description

When using Deck or a MapboxOverlay, destroying the instance results in a memory leak. Detached DOM nodes and Deck instances are never garbage collected because internal luma.gl observers remain permanently attached.

I think there is a problem in the teardown lifecycle when _reuseDevices is enabled:

  1. MapboxOverlay passes _reuseDevices: true to luma.gl. This causes WebGLDevice.destroy() to intentionally abort early so the WebGL context can be cached.
  2. Because the device skips teardown, it does not clean up its associated CanvasContext.
  3. In deck.js:289 (Deck.finalize()), deck.gl cleans up its internal canvas wrapper like this: this._canvasContext = null;
  4. Because deck.gl drops the reference without explicitly calling this._canvasContext.destroy(), the underlying Surface.destroy() method in luma.gl is never triggered.
  5. As a result, _stopObservers() is never called. The CanvasObserver (which attaches a ResizeObserver and matchMedia listener) remains active, creating a strong GC root that holds the detached and Deck instance in memory forever.

I have created a codepen where I found out that the issue is not only in a MapboxOverlay but its in the Deck class.

Flavors

  • Script tag
  • React
  • Python/Jupyter notebook
  • MapboxOverlay
  • GoogleMapsOverlay
  • CARTO
  • ArcGIS

Expected Behavior

If I explicitly call Deck.finalize I expect that all reassures are released.
So Deck.finalize() must explicitly call destroy() on the CanvasContext before nullifying it.

Steps to Reproduce

https://codepen.io/LeLunZ/full/qEqGRYa

Click on "Run 10x" then open dev tools + run garbage collection + click on the correct vm instance on the Bottom right -> take a heap snapshot -> search for canvas, WebGL2RenderingContext, or ResizeObserver.
You will see 10 of each that don't get garbage collected.

You can also check the logs they will show "RO #3 created" but never "RO #3 disconnected"

Environment

  • Framework version: Angular 20 (but also is an issue in native js)
  • Browser: Chrome/Safari
  • OS: Mac OS

Logs

No logs

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions