angular: memory leak with angular animation

šŸž bug report

Description

When using *ngIf to hide element which contains another element with angular animation trigger, appearing DOM nodes that garbage cleaner can`t remove. I inspect amount of DOM nodes by chrome performance monitor. In small app it`s doesn't matter but in app with a lot of components and animations it`s appear to be critical, especially on mobile

šŸ”¬ Minimal Reproduction

Create div which shown on *ngIf; inside it create another div with animation trigger like this: [@trigger]; try to hide and show parent div, while watching performance monitor; you notice that amount of DOM nodes increasing and this nodes can`t be cleaned by garbage collector

https://stackblitz.com/edit/angular-tguytc

šŸŒ Your Environment

Angular Version:



Angular CLI: 7.3.9
Node: 10.15.3
OS: win32 x64
Angular: 7.2.15
... animations, common, compiler, compiler-cli, core, forms
... language-service, platform-browser, platform-browser-dynamic
... router

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.13.9
@angular-devkit/build-angular     0.13.9
@angular-devkit/build-optimizer   0.13.9
@angular-devkit/build-webpack     0.13.9
@angular-devkit/core              7.3.9
@angular-devkit/schematics        7.3.9
@angular/cli                      7.3.9
@ngtools/webpack                  7.3.9
@schematics/angular               7.3.9
@schematics/update                0.13.9
rxjs                              6.3.3
typescript                        3.2.4
webpack                           4.29.0

UPD: One of possible solutions: you can separate child element with animation trigger in new component, so ngDestroy of new component works fine and clear element when needed

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 15
  • Comments: 15 (3 by maintainers)

Most upvoted comments

This issue is driving me crazy for a couple of days now. I have analyzed this behavior a lot and I found out that it isnā€™t only a problem with *ngIf but also with *ngFor (where I faced this issue too) and I guess that it is also a problem with *ngSwitchCase (not tested). I think in general you can say: If you have a parent element which has child elements inside it with animation triggers and the parent element gets removed (e. g. by *ngFor or *ngIf) before the child elements get removed, the child elements with animation triggers attached are causing a DOM leak (in case of *ngFor with a huge list - a massive one).

I guess it doesnā€™t even matter if the parent element itself has an animation trigger attached to it.

For example (the scenario where I faced this issue):

I have a multidimensional array, where each entry of the array is another array. Like:

let arr = [
    ["test01", "test02", "test03"],
    ["test11", "test12", "test13"],
    ["test21", "test22", "test23"],
    ["test31", "test32", "test33"],
]

In my template I use a nested *ngFor to visualize this. Each array is a own div and inside that div each entry of the array is a paragraph. The paragraphs have animation triggers attached to them to animate them at application runtime.

For example:

<div *ngFor="let list of arr"">
    <p [@exampleAnimation]="exampleState *ngFor="let entry of list>{{entry}}</p>
</div

Now, when you do arr.length = 0 to clear the array, all parapgraphs are leaked as Detached DOM elements. If you remove the animation trigger and do the same, that doesnā€™t happen.

This also doesnā€™t happen with an one dimensional array and a single *ngFor.

A workaround that I found out:

First clear all array entries inside the main array, then run change detection and let angular destroy all elements from the inner *ngFor (the paragraphs), then clear the main array and run change detection again so angular can now destroy all elements from the outer *ngFor (the divs). If you do it like that, everything gets cleaned up as expected.

But this workaround feels really hacky so I donā€™t think that it is a good option.

I also had a similar issue with *ngIf, where the parent container with the *ngIf attached had a child element with an animation trigger. This also caused a memory leak (of the whole component template). There I used the same workaround as above: First removed the child element itself with an *ngIf, then run change detection and let angular update the template, and then remove the parent container with the original *ngIf. This also fixed the issue.

But as I said, this workaround isnā€™t a solution because it is really dirty and feels hacky. So I hope this will get fixed soon, because it makes the @angular/animations package almost unusable for large applications with much animations.

also ran into thisā€¦ especially annoying in our case because that loop with the animation is run through on a timer (itā€™s kind of a slideshow situation) which also is endlessā€¦ which means this is guaranteed to crash the browser-tab given enough time šŸ§Ø

Still an issue in Angular 10 as far as I can see. A simple <*ngFor @withAnimation> causes un-GCā€™d detached DOM nodes. I donā€™t work for Google, but Iā€™d question the P3 prioritization of any (easily reproducible) performance bug ā€“ just my 2 cents.

Closing based on Darioā€™s comment above. Please open a new issue (and include a repro) if the problem still exists in the most recent version of Angular. Thank you.

Could somebody take a look at this? @dario-piotrowicz maybe?

@JoostK I see. Can we please bump this up from P3? This issue is easily reproducible and will quickly destroy apps with any moderate-to-heavy use of animations.