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