construct-style-sheets
construct-style-sheets copied to clipboard
Potential Memory Leak?
Hi @calebdwilliams!
Thanks for the important polyfill! Rather than a confirmed bug report, this is more of a question about a potential memory leak in the current implementation.
I have only done a cursory review of the codebase so please correct me if I'm wrong, but it seems the following is true:
- a
Locationobject holds a strong reference to aDocumentorShadowRoot $locationsinConstructedStyleSheet.tsis aWeakMapwhere keys are instances ofConstructedStyleSheetand values are arrays ofLocationinstancesaddAdopterLocationpushes aLocationobject (including its strong reference to aDocumentorShadowRoot) to theLocationarray that is mapped in$locations(such mapping from the key of the specificConstructedStyleSheetadopted by the sameDocumentorShadowRootstrongly referred to by the Location object being pushed to the array)addAdopterLocationis called when a stylesheet is initially adopted (or when any 'adopted' style node is removed)removeAdopterLocationis the only place that theLocationobjects (and their strong reference to aDocumentorShadowRoot) are removed from the array that is mapped in$locationsremoveAdopterLocationis called when aConstructedStyleSheetis removed from aDocumentorShadowRoot'sadoptedStylesheetsbut is not called in any other case
Based on the above, it seems that in the following scenario we would have a memory leak:
- Custom element instance 1 is constructed and a
ConstructedStyleSheetis constructed and adopted by Custom element instance 1'sshadowRoot - Custom Element instances 2 - 1,000,000 are constructed and added and then later removed from the DOM, where each of custom element 2 - 1,000,000's constructors cause their shadow roots to adopt the same
ConstructedStyleSheetfrom step 1
As a WeapMap's values are strongly held until the key is referred to only by the WeakMap itself, if Custom element instance 1 is still strongly held in the DOM after instances 2 - 1,000,000 are no longer held in the DOM or referenced by anything but this polyfill, it would seem that this polyfill would cause instances 2 - 1,000,000's not to be garbage collected. This is because each of instances 2 - 1,000,000's shadow root will still be referenced by the Location objects in the array that is the value of the $locations entry with the key of the ConstructedStyleSheet still adopted by custom element instance 1's shadow root. And because shadowRoot refers to the element it is attached to with the ShadowRoot.host property, the element itself will not be garbage collected.
If this is the case, this would seem to be a serious issue as adopting a StyleSheet when a custom element is constructed is a major use case for adopted stylesheets and constructable stylesheets so that all instances of a custom element constructor can share a single set of styles. At the very least a warning not to use this polyfill with short lived custom elements may be in order.
Please forgive me if I've missed something or if any of this is unclear. Also, unfortunately it doesn't seem we can force garbage collection so a test case where memory is not being garbage collected doesn't seem to be proof of a memory leak so we seem to be forced to report potential issues with analysis like the above: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management