Understanding Memory Leaks in Angular: A Concise Guide with Examples

Vikas Tiwari
4 min readMay 7, 2023

Memory leaks can be a common issue in Angular applications if not handled properly. They can gradually degrade performance, cause unexpected crashes, and consume excessive memory resources. In this blog, we will explore what memory leaks are, why they occur in Angular, and how to identify and prevent them. Let’s dive in!

What is a Memory Leak?

A memory leak occurs when allocated memory is no longer needed but not released, leading to memory consumption that grows over time. In Angular, memory leaks often happen when components or other objects are not properly disposed of, preventing the garbage collector from reclaiming memory.

Common Causes of Memory Leaks in Angular:

a) Subscriptions: Not unsubscribing from observables or event listeners can keep references alive, causing memory leaks. Ensure you unsubscribe from subscriptions in the ngOnDestroy lifecycle hook.

Example:

private subscription: Subscription;

ngOnInit() {
this.subscription = someObservable.subscribe();
}

ngOnDestroy() {
this.subscription.unsubscribe();
}

b) DOM Event Handlers: Attaching event handlers directly to DOM elements without proper cleanup can lead to memory leaks. Remove event listeners when the component is destroyed.

Example:

ngOnInit() {
this.onClickHandler = this.onClick.bind(this);
document.addEventListener('click', this.onClickHandler);
}

ngOnDestroy() {
document.removeEventListener('click', this.onClickHandler);
}

c) Retaining References: Holding onto unnecessary references, such as storing objects in global variables or long-lived services, can prevent their proper disposal. Dereferencing or Nulling such references is suggested.

Example:

@Injectable()
export class DataService {
private cachedData: any;

// ...

storeData(data: any) {
this.cachedData = data; // Avoid unnecessary retention
}
}

Detecting Memory Leaks:

To identify memory leaks, you can use browser profiling tools like Chrome DevTools. Monitor memory usage over time and look for continuous growth or sudden spikes. Inspect retained objects to find potential culprits.

Detecting Memory Leaks with Chrome DevTools: Chrome DevTools offers several features that help identify memory leaks:

  • Memory panel: Monitor memory usage and track potential memory leaks over time. In Chrome’s inspector go to the performance tab and start recording, now perform some actions on the website and then stop the recording to view the memory usage graph over time.
Memory usage
  • Heap snapshots: Capture snapshots of the JavaScript heap and compare them to identify retained objects.
Heap snapshots — 1
Heap snapshots — 2

You can also view the list of retainers when you select any object from the above snapshot. If you find any object that retains a large size that you don’t expect, it may be the problem and you can view here what reference is holding it from being collected.

Retainers list

You can find the reference of the selected node in two ways.
1 — Just hovering over the node.

Hover

2 — Simply type $0 in the console to get the reference of the selected node.

Type $0
  • Allocation timelines: Analyze object creation and understand how memory usage evolves.
Allocation timelines

Preventing Memory Leaks:

a) Proper Component Lifecycle Management: Ensure that components unsubscribe from subscriptions, detach event handlers, and release any held references in the ngOnDestroy lifecycle hook.

ngOnDestroy() {
this.subscription.unsubscribe();
}

b) Use RxJS Operators: Utilize operators like takeUntil or takeWhile to automatically unsubscribe from observables when a specific condition is met or when the component is destroyed.

Example:

import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

private unsubscribe$: Subject<void> = new Subject<void>();

ngOnInit() {
someObservable.pipe(takeUntil(this.unsubscribe$)).subscribe();
}

ngOnDestroy() {
this.unsubscribe$.next();
this.unsubscribe$.complete();
}

c) Angular Dependency Injection: Use dependency injection to manage the services lifecycle and ensure they are properly cleaned up when no longer needed.

Example:

// service example
@Injectable({
providedIn: 'root'
})
class TestService {}
@Component({ … })
class TestComponent {
constructor(private service: TestService) {}
}

Conclusion:

Memory leaks can adversely impact Angular applications if left unaddressed. By understanding the causes, detecting them early, and following best practices for component lifecycle management, you can prevent memory leaks and improve the performance and stability of your Angular applications.

Remember, proactive memory management is crucial to keep your Angular app running smoothly and efficiently. Hope you extracted some knowledge from it. In case of any doubt or if you just want to say Hi! feel free to reach me on LinkedIn or GitHub.

If you like the blog make sure to take a look at my YouTube channel for more amazing stuff.

--

--