Memory management in Angular applications

Javascript is a high level language that doesn’t include memory management features. Memory gets automatically allocated when the program creates an object or released when the program no longer needs it. In order to deallocate that memory it relies in the Garbage Collector to decide when to release an object from memory. Basically what the GC does is look for values that do not have any references on them (they do not exist anymore from the program point of view) so they get released from memory.

Most of the time this recollection of resources happens without any issues but there are situations when the GC cannot decide if an object should be released or not. Then the memory footprint of a program might grow unexpectedly and produce performance problems or crashes.

Is in the hand of the developer then to prevent and solve this issues.

When does Angular release a component from memory?

One common pattern in angular applications is to use ng-if to manage the visibility of parts of the application. When passed a falsy value, ng-if will remove its child element and will also call the $destroy event in its child scopes.

Also the ng-view directive of the angular router will replace the html elements of the transcluded contents when changing the route and also call the $destroy on the transcluded scope.

The $destroy function nullifies the scope reference to the parent scope, severing the tree connection downwards. As that scope do not longer has any references on it, it will be marked as collectible and released eventually by the GC.

Event listeners

The most likely culprit of a memory leak in Angular is adding an event listener to a DOM object. If you forget to remove the listener on the $destroy event of your directive, it will hold a reference to a DOM node even if it is removed from the document. The DOM tree will then become a “Detached DOM tree” and will leak.

Modern JS engines are able to figure most of this situations for you and remove the listeners, but more complex tree hierarchies can challenge even the best GC.

Subscriptions

If you use a library that exposes a subscription interface (Redux, RxJS, etc.) you have to remember to dispose the subscriptions on component removal.

Strategies to prevent leaks

  • Nullify all references to models in a component on the component $destroy phase

  • Try to remove all circular dependencies by using yet another layer of indirection

  • Create a this.state object to manage the instance state in a place for easier nullification

  • Dispose subscriptions

  • Examine third party software and decide to use it or not also based on memory problems or contribute with PRs

Last updated