Search code examples
angularmemory-leaksangular-compiler

Angular - Dynamic Component - Compiler Issue - Memory Leak?


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.


Solution

  • 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.