Open bendemboski opened 10 months ago
I haven't looked in to this too deeply yet, but here is the manager for plain functions:
I wonder if the manager infra is holding on to the function reference
Upon further snooping, I found a helper definition cache
idk if you want to dig in to glimmer, but resolving any sort of memory leak would be a huge help <3 :muscle:
as an aside, how do find out what is still holding on to a reference?
My stab at a fix: https://github.com/glimmerjs/glimmer-vm/pull/1440
π Describe the Bug
Anytime a component that has a local modifier or helper is rendered, that modifier/helper object/function will be retained in memory even after the component is un-rendered (it is released when the app exits). The fact that it is retained after the component is un-rendered is why I'm calling it a memory leak.
This has two implications that, especially when considered together, can cause quite a lot of memory to leak:
readonly myHelper = function myHelper() { return 'hello'; }
, then a copy of themyHelper
function will leak for each component instance that is rendered/un-renderedthis
), then the component instance itself will leak㪠Minimal Reproduction
This repository contains a minimal reproduction of the issue, both via an integration test, and steps for a manual repro in the dummy app.
π Actual Behavior
After rendering and un-rendering a component that invokes a local helper, that local helper function leaks.
π€ Expected Behavior
It should be released when the component is un-rendered.
π Environment
β Additional Context
I dug in some and believe I have a diagnosis of the problem. Here is the culprit retainer chain:
The
CompileTimeCompilationContextImpl
is effectively a singleton, as isConstantsImpl
. When the local helper is first invoked,ConstantsImpl
'shelper()
method is invoked, which calls intovalue()
, which puts an object containing the helper arrow function in athis.values
andthis.indexMap
:They are not removed until the app exits.
This is some guesswork, but it appears that these helpers are being stored in the same place as global helpers, so the pattern is to keep them cached forever, but really they should be stored in a place/manner such that their lifetime is bound to the lifetime of the component, and/or they should stored via weak references.