Skip to content

addEventListener with AbortSignal option can cause memory leaks #54739

@jblas

Description

@jblas

Which @angular/* package(s) are the source of the bug?

zone.js

Is this a regression?

Yes

Description

Our application is using [email protected] outside of Angular, as a dependency of OpenTelemetry.

In our application we noticed memory leaks involving large subtrees of detached HTMLElements after updating to [email protected]. 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:

  • Press the remove button in the test case
  • Open the Web Inspector Memory Panel
  • Hit the Garbage Collect button
  • Trigger a Heap Snapshot

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

Metadata

Metadata

Assignees

Labels

area: zonesIssues related to zone.jsregressionIndicates than the issue relates to something that worked in a previous version

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions