stencil-store icon indicating copy to clipboard operation
stencil-store copied to clipboard

bug: Memory leak sharing store instance

Open nwhittaker opened this issue 11 months ago • 1 comments

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.

nwhittaker avatar Feb 25 '25 16:02 nwhittaker

@nwhittaker thanks for raising the issue. Any contributions that may resolve this problem are much appreciated.

christian-bromann avatar Feb 28 '25 00:02 christian-bromann

@christian-bromann Hi I think this issue has been fixed in #661, could you help release a patch version for this change? Thanks

stout-ni avatar Dec 10 '25 00:12 stout-ni

The fix via #661 has been released now :) v2.2.2

johnjenkins avatar Dec 10 '25 10:12 johnjenkins