2

I am trying to create a dynamic component. Here is the sample in plunker. http://embed.plnkr.co/EVFglgkp24hkRkpxrEGe/ Everything is working but there is a memory leak.

Here is the github ticket https://github.com/angular/angular/issues/19997

Dynamically created components are getting destroyed, but the component which created the dynamic component is not getting destroyed. In other words, The component which compiled the dynamic component is not getting destroyed.

In the above example, if we navigate back and forth between "Home" and "Dynamic Page" and take a snapshot of memory in chrome, you can see that the components suppose to be destroyed are still there as depicted below.

enter image description here

For the testing purpose, I even tried commenting the bellow lines, but still the issue exists.

      let injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector);
      let ngMdlRef = ngMdlFac.create(injector);
      let cmpFactory = ngMdlRef.componentFactoryResolver.resolveComponentFactory(DynamicHtmlComponent);
      this.cmpRef = this.vcRef.createComponent(cmpFactory);

The moment I call

this.compiler.compileModuleAsync

The creator component is not getting destroyed at all. until then there is no issue.

Can you please someone help on this. Thank you in advance.

Rajkumar
  • 153
  • 8

1 Answers1

5

You are right, the problem with the memory leak is caused by the manual module compilation and instantiation. If you take a look at the retainers of the MyCreatorComponent you can see that functions DynamicHtmlComponent and DynamicModule hold the reference to the parent MyCreatorComponent function through context.

enter image description here

These are the objects with the shortest distance to GC root, so they are most likely the objects responsible for memory leaks. The question is why they are not removed? The answer is that Angular heavily caches everything it creates and it happens in your case as well. Just through a quick look I've identified at least two caches that retain the references.

First

export class JitCompiler {
  private _compiledHostTemplateCache = new Map<Type, CompiledTemplate>();

When you call this.compiler.compileModuleAsync(DynamicModule) Angular adds CompiledTemplate with the key DynamicHtmlComponent to this cache and never clears it.

Second

const _tokenKeyCache = new Map<any, string>();

When you call var ngMdlRef = ngMdlFac.create(...) Angular adds DynamicHtmlComponentFactory to this caches and never deletes it.

Notice that these caches are Maps, not WeakMaps so as long as there's no explicit call to .delete() the objects will be retained.

Max Koretskyi
  • 101,079
  • 60
  • 333
  • 488
  • While we can run `compiler.clearCache` to clear `compiledHostTemplateCache` and other stuff cached within compiler, we can't clear `tokenKeyCache`. – yurzui Oct 28 '17 at 19:01
  • @yurzui, yeah, and these are only the two I've found. I think there are others – Max Koretskyi Oct 28 '17 at 19:08
  • Angular will add all tokens in `tokenKeyCache` for providers every time it executes compilation I think it is because that compiler is supposed to be run only once – yurzui Oct 28 '17 at 19:09
  • Also we need to enable prod mode – yurzui Oct 28 '17 at 19:10
  • @yurzui, yeah, it seems we won't be able to avoid memory leaks with this approach – Max Koretskyi Oct 28 '17 at 19:12
  • He already posted issue on github. Will see what they can answer – yurzui Oct 28 '17 at 19:13
  • @yurzui, can you put a reference here? – Max Koretskyi Oct 28 '17 at 19:15
  • `ComponentFactory`, `DynamicModule` and `DynamicHtmlComponent` are added to tokenKeyCache – yurzui Oct 28 '17 at 19:25
  • @yurzui, didn't want to go deeper into this :) – Max Koretskyi Oct 28 '17 at 19:30
  • Ok, i don't mind. As you have already said `Angular heavily caches everything it creates` – yurzui Oct 28 '17 at 19:31
  • @yurzui, I was just curios enough to make use of `Retainers` section to identify objects holding a reference to the `MyCreatorComponent` – Max Koretskyi Oct 28 '17 at 19:33
  • Thank you @AngularInDepth.com & yurzui for helping me to understand this issue in detail. As yurzui mentioned, I created ticket in github, lets see... special thanks to AngularInDepth.com for your articles. I gone through some of your articles when I was searching for dynamic components. :-) – Rajkumar Oct 30 '17 at 15:22
  • @Rajkumar, glad I could help, good luck! I also posted the same answer on github :) Follow angluarindepth.com publication for more great articles to come – Max Koretskyi Oct 30 '17 at 15:54
  • @Rajkumar, don't forget you can accept my answer if it answered you question completely :) – Max Koretskyi Nov 05 '17 at 07:30