How to Prevent Memory Leak in Angular: Best Practices
unsubscribe from observables and event listeners when components are destroyed, typically inside the ngOnDestroy lifecycle hook. Using async pipe or takeUntil operator helps manage subscriptions automatically and keeps your app memory efficient.Why This Happens
Memory leaks in Angular happen when subscriptions to observables or event listeners are not properly cleaned up. This causes the app to keep references to components or services even after they are no longer needed, wasting memory and slowing down the app.
import { Component, OnInit, OnDestroy } from '@angular/core'; import { interval, Subscription } from 'rxjs'; @Component({ selector: 'app-leaky', template: '<p>Leaky Component</p>' }) export class LeakyComponent implements OnInit, OnDestroy { private subscription!: Subscription; ngOnInit() { this.subscription = interval(1000).subscribe(val => { console.log('Tick', val); }); } // Missing ngOnDestroy to unsubscribe ngOnDestroy() { // Intentionally left blank to show missing unsubscribe } }
The Fix
To fix memory leaks, unsubscribe from observables in the ngOnDestroy lifecycle hook. Alternatively, use the async pipe in templates or the takeUntil operator in code to manage subscriptions automatically.
import { Component, OnInit, OnDestroy } from '@angular/core'; import { interval, Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; @Component({ selector: 'app-fixed', template: '<p>Fixed Component</p>' }) export class FixedComponent implements OnInit, OnDestroy { private destroy$ = new Subject<void>(); ngOnInit() { interval(1000) .pipe(takeUntil(this.destroy$)) .subscribe(val => { console.log('Tick', val); }); } ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); } }
Prevention
Always unsubscribe from subscriptions in ngOnDestroy or use Angular's async pipe in templates to handle it automatically. Use the takeUntil operator with a Subject to manage multiple subscriptions cleanly. Avoid creating long-lived subscriptions inside components without cleanup. Use linting tools like rxjs-angular to warn about missing unsubscriptions.
Related Errors
Other common issues include not cleaning up event listeners added with Renderer2 or native DOM methods, which also cause leaks. Forgetting to unsubscribe from Router events or NgZone subscriptions can cause similar problems. Always pair resource allocation with cleanup.