Skip to content

bug: Memory leak sharing store instance #573

@nwhittaker

Description

@nwhittaker

Prerequisites

Stencil Store Version

2.0.16

Stencil Version

4.0.27

Current Behavior

Given a store instance that is shared between multiple components, any disconnected components can remain detached in memory until the store's dispose() function is called.

Expected Behavior

A component with a shared store is able to be garbage collected when it's disconnected.

Steps to Reproduce

  1. Checkout the repo and switch to the @stencil-store/disconnected-memleak branch, if needed.
  2. In the project, run npm install and npm start.
  3. In the browser (e.g. Chrome 133), open devtools.
  4. In the viewport, click the Create my-global-counter button and optionally click the button that appears.
  5. Click the Destroy my global-counter button.
  6. Optionally repeat steps 4 and 5 a few times.
  7. Optionally wait 2s to give the store's internal clean-up logic to run.
  8. In the devtools Memory tab, click the Collect garbage button and note that no "Garbage collected" messages are logged despite the existence of a FinalizationRegistry in index.html.
  9. Take a heap snapshot, search in the report for "counter", and note one or more MyGlobalCounter and Detached <my-global-counter> entries exist.

Code Reproduction URL

https://github.com/nwhittaker/stencil-component/tree/%40stencil-store/disconnected-memleak

Additional Information

The store's elmsToUpdate map looks to be what is retaining the component. Additionally, a couple issues appear to be at play preventing the map from being cleaned up in this scenario:

  1. Disconnecting a component doesn't trigger the store to run it's clean-up logic.
  2. When the store's clean-up logic does next run (e.g. on the next set), it doesn't identify the component as disconnected.

I don't know if using a WeakReference for the component is feasible. It's also not clear to me why the isConnected logic is checking for isConnected on the component instance instead of the component's element:

https://github.com/ionic-team/stencil-store/blob/74b752566e113f8e22b786a15b44fd01c2e79b4e/src/subscriptions/stencil.ts#L14

If I naively wrap maybeElement in the @stencil/core getElement() function, I'm able to resolve the 2nd issue that's preventing garbage collection.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Bug: ValidatedThis PR or Issue is verified to be a bug within Stencil StoreHelp Wanted

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions