-
Notifications
You must be signed in to change notification settings - Fork 26.2k
addEventListener with AbortSignal option can cause memory leaks #54739
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Until a better solution is found, Unfortunately, that workaround could still leave "dead" listeners on the signal object. |
…s removed This commit updates the implementation of the `addEventListener` patcher. We're currently creating an abort event listener on the signal (when it's provided) and never remove it. The abort event listener creates a closure which captures `task` (tasks capture zones and other stuff too) and prevent `task`, zones and signals from being garbage collected. We now store the function which removes the abort event listener when the actual event listener is being removed. The function is stored on task data since task data is already being used to store different types of information that's necessary to be shared between `addEventListener` and `removeEventListener`. Closes angular#54739
…s removed This commit updates the implementation of the `addEventListener` patcher. We're currently creating an abort event listener on the signal (when it's provided) and never remove it. The abort event listener creates a closure which captures `task` (tasks capture zones and other stuff too) and prevent `task`, zones and signals from being garbage collected. We now store the function which removes the abort event listener when the actual event listener is being removed. The function is stored on task data since task data is already being used to store different types of information that's necessary to be shared between `addEventListener` and `removeEventListener`. Closes angular#54739
…s removed (#55339) This commit updates the implementation of the `addEventListener` patcher. We're currently creating an abort event listener on the signal (when it's provided) and never remove it. The abort event listener creates a closure which captures `task` (tasks capture zones and other stuff too) and prevent `task`, zones and signals from being garbage collected. We now store the function which removes the abort event listener when the actual event listener is being removed. The function is stored on task data since task data is already being used to store different types of information that's necessary to be shared between `addEventListener` and `removeEventListener`. Closes #54739 PR Close #55339
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
Which @angular/* package(s) are the source of the bug?
zone.js
Is this a regression?
Yes
Description
Our application is using zone.js@0.14.3 outside of Angular, as a dependency of OpenTelemetry.
In our application we noticed memory leaks involving large subtrees of detached HTMLElements after updating to zone.js@0.14.3. Version 0.14.2 does not have an issue.
The issue seems to be related to the fact that zone.js registers an "abort" handler on the AbortSignal object passed in to the addEventListener() call and the callback it is registering captures the task in its scope. This handler is as far as I can tell never removed off the AbortSignal object.
https://github.com/angular/angular/blob/zone.js-0.14.3/packages/zone.js/lib/common/events.ts#L488
The big issue I see with the current implementation is that zone.js has no clue what the lifetime and ownership model is for the AbortSignal object in question or the element that is captured in the task within the callback that is never removed.
In our particular case the detached HTMLElement captured within the task is part of a larger subtree that gets removed/detached. The signal itself is owned by some code associated with an ancestor element that out-lives the subtree that is removed, so as long as that listener callback is alive, so is the task, the element it captures, and any other elements in the detached subtree the element is still referencing via parentNode, next/prevSibling, etc.
Also, we can't assume that this "abort" listener will be removed via a corresponding element's removeEventListener() call because there may never be a removeEventListener() call made ... and in most circumstances when a subtree of elements is detached, this would be ok and things would garbage collect just fine.
The issue can be observed with this simple test case:
addeventlistener-signal-bug.txt
If you load the test case into Chrome:
In the resulting Heap Snapshot you will notice that there are detached HTMLDiv, HTMLButton, HTMLUL, and HTMLLI elements listed.
If you remove the include of zone.js from the test case, and then repeat the steps above, you will see that the elements mentioned above are no longer listed in the Heap Snapshot, meaning they were garbage collected properly.
Please provide a link to a minimal reproduction of the bug
No response
Please provide the exception or error you saw
No response
Please provide the environment you discovered this bug in (run
ng version
)No response
Anything else?
No response
The text was updated successfully, but these errors were encountered: