angular: Components not being garbage collected by browser, causing major memory leak
Which @angular/* package(s) are the source of the bug?
Don’t know / other
Is this a regression?
No
Description
Stack Overflow Post: https://stackoverflow.com/questions/71026959/memory-leak-garbage-collection-in-browser-not-collecting-components
Summary:
- when a component gets created with *ngIf=“true” or with ViewContainerRef.createComponent(componentFactory: ComponentFactory<C>), and then destroyed, with *ngIf=“false” or with ViewContainerRef.clear(), the component stays in memory, and can’t be garbage collected by the browser
- this causes a significant memory leak (especially in larger apps that use many nested components)
- this is a tricky bug because:
- if you download the minimal reproduction app, either a or b will happen a. it will leak memory right away, and components won’t be garbage collected (regularly or with the manual garbage collection button provided by most browsers for debugging purposes) b. it won’t leak memory right away, and it will happily garbage collect components regularly or with the manual garbage collection button provided by most browsers for debugging. After enough time has elapsed, or after you’ve restarted the app an arbitrary amount of times, it will eventually get into the memory leaking state
- therefore, if my coworkers and I download the repo, then run the app, it seems to be a coin toss on whether or not this bug gets reproduced right away
- if you’re one of the lucky few that doesn’t get it to happen right away, perhaps try it on a different computer, or wait a couple minutes and refresh the web app a couple times
- the fact that it doesn’t happen first try every time makes it hard to prove that there is a bug with this minimal reproduction app
- I’ve included screen shots in the stack overflow post (near the bottom), that proves that this is indeed happening
- it does however happen every time with a much larger production app that I’m working on
Please provide a link to a minimal reproduction of the bug
Repo: https://github.com/kevinpbaker/angular-memory-killer S3 bucketed app: https://angular-memory-killer.s3.us-west-2.amazonaws.com/index.html
Please provide the exception or error you saw
There is no error thrown. The components in memory aren't being garbage collected, and will eventually crash the browser window.
Please provide the environment you discovered this bug in (run ng version
)
I have upgraded this project to be the latest version of angular 12, and the latest version of angular 13, and it leaks in 11, 12, and 13. I have also tried it on multiple computers (M1 mac, older intel mac, windows pc, windows laptop).
Angular CLI: 11.2.18
Node: 16.14.0
OS: darwin arm64
Angular: 11.2.14
... animations, common, compiler, compiler-cli, core, forms
... language-service, platform-browser, platform-browser-dynamic
... router
Ivy Workspace: Yes
Package Version
---------------------------------------------------------
@angular-devkit/architect 0.1102.18
@angular-devkit/build-angular 0.1102.18
@angular-devkit/core 11.2.18
@angular-devkit/schematics 11.2.18
@angular/cdk 11.2.13
@angular/cli 11.2.18
@angular/material 11.2.13
@schematics/angular 11.2.18
@schematics/update 0.1102.18
rxjs 6.5.5
typescript 4.0.8
Anything else?
I’ve included a link to a stack overflow post I made that adds additional information and pictures to show that it is indeed leaking memory.
Microsoft Edge has an experimental feature called “Detached Elements” that can be enabled to see any objects that are dangling and ready to be garbage collected. You can use this feature to visually see that the angular components aren’t being garbage collected (I’ve also included screen shots of this in the stack overflow post).
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Reactions: 10
- Comments: 21 (10 by maintainers)
@kevinpbaker thanks for the update!
Chromium bug: https://bugs.chromium.org/p/chromium/issues/detail?id=1308845
This issue has been fixed within the chromium browser. It hasn’t been released yet, but it can be tested with Chrome’s Canary browser: https://www.google.com/intl/en_ca/chrome/canary/
Broken Version 100.0.4896.60 (Official Build) (arm64)
https://user-images.githubusercontent.com/48214072/162014156-d33d5521-a5fc-4554-ade2-c7a943923a16.mp4
Fixed Version 102.0.4987.0 (Official Build) canary (arm64)
https://user-images.githubusercontent.com/48214072/162014327-751da40f-35fd-429a-8a1c-9fcc32225b43.mp4
Interesting, #41047 does mention that the leaks were specific to Chrome. It may be worth checking again after next week’s 14.0.0-next release which contains the
__ngContext__
fix. You can also try it now by installing from https://github.com/angular/core-builds.@crisbeto after a lot more testing, I think this issue is specific to Microsoft Edge and Chrome. I was unable to get Safari or Firefox to leak memory using the memory kill app provided.
Safari
The memory footprint in Safari will always climb and then plateau. Even if I spam the garbage collection button it remains at or below the plateau value. Here is an image showing this behavior:
Firefox
The memory footprint in Firefox gets cleaned up regardless of how many times I try to get it into the leaking memory state. Here is an image showing this behavior:
While doing this I had all 4 browsers running at the same time. All of them running the memory killer app. Microsoft Edge and Chrome would leak memory, but Safari and Firefox would not.
From the tests and information above, I think we can conclude that this isn’t an Angular Framework problem, but a browser problem.
Also, one of the computers is factory new that I used to leak memory on Microsoft Edge and Chrome. I install node.js, then I clone the memory killer app using
git clone https://github.com/kevinpbaker/angular-memory-killer.git
, then I runnpm install
andng serve --prod --port 4204
. I am able to get Microsoft Edge and Chrome to leak memory doing this.To answer the questions:
__ngContext__
fix would resolve the issue in the memory killer app, but it isn’t foolproof. The actual solution is in #41047.__ngContext__
doesn’t cause memory leaks by itself. If the app doesn’t have leaks at all,__ngContext__
isn’t going to introduce new leaks. It comes into play if the apps has some leaks which__ngContext__
will make worse.__ngContext__
issue. We have one code path where a reference to the host DOM node will be retained when there are styles associated with it. It only applies to components withencapsulation: ViewEncapsulation.ShadowDom
though.As for reproducing it, it’s entirely possible that I’m not doing something in the same way as you or there’s something in my environment that prevents it from happening. What I did today was to
npm i
,ng serve
and then take memory snapshots in the dev tools. I had it running for about 15min with no change in the amount memory being consumed.One thing worth trying is to check if the leak happens in other browsers like Firefox or Safari. It would at least help us narrow it down to a framework issue or a potential problem with a browser.
I tried running both the memory killer app and the menu app mentioned above, but I couldn’t get it to leak more than 13 DOM nodes which would usually be GCed on the next snapshot. There’s a recording of my timeline below.
https://user-images.githubusercontent.com/4450522/154483715-0773ccc2-4a64-4bf1-b02d-0e817e980513.mov
Looking at your heap dump, it does have some references in
__ngContext__
so it’s possible that https://github.com/angular/angular/pull/45051 will help with it. It’s hard to say before the change is actually released though. You could try changing theMemoryKillerComponent
to the following which should have a similar effect: