bug: Memory leak sharing store instance
Prerequisites
- [x] I have read the Contributing Guidelines.
- [x] I agree to follow the Code of Conduct.
- [x] I have searched for existing issues that already report this problem, without success.
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
- Checkout the repo and switch to the
@stencil-store/disconnected-memleakbranch, if needed. - In the project, run
npm installandnpm start. - In the browser (e.g. Chrome 133), open devtools.
- In the viewport, click the Create my-global-counter button and optionally click the button that appears.
- Click the Destroy my global-counter button.
- Optionally repeat steps 4 and 5 a few times.
- Optionally wait 2s to give the store's internal clean-up logic to run.
- In the devtools Memory tab, click the Collect garbage button and note that no "Garbage collected" messages are logged despite the existence of a
FinalizationRegistryinindex.html. - Take a heap snapshot, search in the report for "counter", and note one or more
MyGlobalCounterandDetached <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:
- Disconnecting a component doesn't trigger the store to run it's clean-up logic.
- 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 thanks for raising the issue. Any contributions that may resolve this problem are much appreciated.
@christian-bromann Hi I think this issue has been fixed in #661, could you help release a patch version for this change? Thanks
The fix via #661 has been released now :) v2.2.2