Memory Management 🧠
Effective memory management prevents memory leaks, reduces application bloat, and ensures smooth performance. Learn to identify and fix memory issues in Angular applications.
🎯 Understanding Memory Leaks
Section titled “🎯 Understanding Memory Leaks”Common Causes:
- Unsubscribed observables
- Event listeners not removed
- Timers not cleared
- Detached DOM references
- Circular references
- Global variables
Symptoms:
- Increasing memory usage over time
- Slow performance
- Browser crashes
- Unresponsive UI
🚀 Subscription Management
Section titled “🚀 Subscription Management”Manual Unsubscribe
Section titled “Manual Unsubscribe”import { Component, OnDestroy } from '@angular/core';import { Subscription } from 'rxjs';
@Component({ selector: 'app-data', standalone: true})export class DataComponent implements OnDestroy { private subscription = new Subscription();
ngOnInit() { // Add subscriptions this.subscription.add( this.dataService.getData().subscribe(data => { console.log(data); }) );
this.subscription.add( this.userService.getUser().subscribe(user => { console.log(user); }) ); }
ngOnDestroy() { // ✅ Clean up all subscriptions this.subscription.unsubscribe(); }}takeUntilDestroyed (Modern)
Section titled “takeUntilDestroyed (Modern)”import { Component } from '@angular/core';import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ selector: 'app-modern', standalone: true})export class ModernComponent { constructor() { // ✅ Automatically unsubscribes on destroy this.dataService.getData() .pipe(takeUntilDestroyed()) .subscribe(data => console.log(data)); }}Async Pipe (Best)
Section titled “Async Pipe (Best)”@Component({ selector: 'app-users', standalone: true, imports: [AsyncPipe], template: ` @if (users$ | async; as users) { @for (user of users; track user.id) { <div>{{ user.name }}</div> } } `})export class UsersComponent { users$ = this.userService.getUsers(); // ✅ Async pipe handles subscription automatically}🎨 Real-World Examples
Section titled “🎨 Real-World Examples”Note: Always clean up resources to prevent memory leaks.
1. Event Listener Cleanup
Section titled “1. Event Listener Cleanup”Let’s properly manage DOM event listeners.
import { Component, ElementRef, OnDestroy, inject } from '@angular/core';
@Component({ selector: 'app-scroll-tracker', standalone: true})export class ScrollTrackerComponent implements OnDestroy { private el = inject(ElementRef); private scrollHandler = this.onScroll.bind(this);
ngOnInit() { // ✅ Store reference to remove later window.addEventListener('scroll', this.scrollHandler); }
onScroll() { console.log('Scrolled'); }
ngOnDestroy() { // ✅ Remove event listener window.removeEventListener('scroll', this.scrollHandler); }}2. Timer Cleanup
Section titled “2. Timer Cleanup”Let’s manage setInterval and setTimeout properly.
@Component({ selector: 'app-timer', standalone: true})export class TimerComponent implements OnDestroy { private intervalId?: number; private timeoutId?: number;
ngOnInit() { // ✅ Store timer IDs this.intervalId = window.setInterval(() => { console.log('Tick'); }, 1000);
this.timeoutId = window.setTimeout(() => { console.log('Delayed'); }, 5000); }
ngOnDestroy() { // ✅ Clear timers if (this.intervalId) { clearInterval(this.intervalId); } if (this.timeoutId) { clearTimeout(this.timeoutId); } }}3. Detached DOM References
Section titled “3. Detached DOM References”Let’s avoid holding references to removed DOM elements.
@Component({ selector: 'app-dynamic', standalone: true})export class DynamicComponent implements OnDestroy { private elementRef?: HTMLElement;
createElement() { this.elementRef = document.createElement('div'); document.body.appendChild(this.elementRef); }
ngOnDestroy() { // ✅ Remove DOM element and clear reference if (this.elementRef) { this.elementRef.remove(); this.elementRef = undefined; } }}4. WeakMap for Caching
Section titled “4. WeakMap for Caching”Let’s use WeakMap to prevent memory leaks in caches.
@Injectable({ providedIn: 'root' })export class CacheService { // ✅ WeakMap allows garbage collection private cache = new WeakMap<object, any>();
set(key: object, value: any) { this.cache.set(key, value); }
get(key: object) { return this.cache.get(key); }
// When key object is garbage collected, // the cache entry is automatically removed}✅ Best Practices
Section titled “✅ Best Practices”1. Always Unsubscribe
Section titled “1. Always Unsubscribe”// ✅ Good - Use takeUntilDestroyedthis.data$.pipe(takeUntilDestroyed()).subscribe();
// ✅ Good - Use async pipetemplate: `{{ data$ | async }}`
// ❌ Avoid - Naked subscriptionthis.data$.subscribe(); // Memory leak!2. Clean Up Event Listeners
Section titled “2. Clean Up Event Listeners”// ✅ Good - Remove listenersngOnDestroy() { window.removeEventListener('resize', this.handler);}
// ❌ Avoid - Leaving listenersngOnDestroy() { // No cleanup - memory leak!}3. Clear Timers
Section titled “3. Clear Timers”// ✅ Good - Clear all timersngOnDestroy() { clearInterval(this.intervalId); clearTimeout(this.timeoutId);}4. Use WeakMap/WeakSet
Section titled “4. Use WeakMap/WeakSet”// ✅ Good - Allows garbage collectionprivate cache = new WeakMap();
// ❌ Avoid - Prevents garbage collectionprivate cache = new Map();5. Avoid Global Variables
Section titled “5. Avoid Global Variables”// ✅ Good - Scoped to componentprivate data: Data[];
// ❌ Avoid - Global pollutionwindow.myData = [];🔍 Detecting Memory Leaks
Section titled “🔍 Detecting Memory Leaks”Chrome DevTools Memory Profiler
Section titled “Chrome DevTools Memory Profiler”// 1. Open Chrome DevTools// 2. Go to Memory tab// 3. Take heap snapshot// 4. Perform actions// 5. Take another snapshot// 6. Compare snapshots// 7. Look for detached DOM nodes// 8. Identify growing objectsAngular DevTools
Section titled “Angular DevTools”// 1. Install Angular DevTools extension// 2. Open DevTools// 3. Go to Angular tab// 4. Check component tree// 5. Look for components not destroyed// 6. Monitor change detection cycles🎯 Memory Optimization Checklist
Section titled “🎯 Memory Optimization Checklist”- Use takeUntilDestroyed() for subscriptions
- Prefer async pipe over manual subscriptions
- Remove event listeners in ngOnDestroy
- Clear all timers and intervals
- Remove detached DOM references
- Use WeakMap/WeakSet for caching
- Avoid global variables
- Profile memory usage regularly
- Fix circular references
- Clean up third-party library instances
🎓 Learning Checklist
Section titled “🎓 Learning Checklist”- Understand common memory leak causes
- Use takeUntilDestroyed for subscriptions
- Implement proper cleanup in ngOnDestroy
- Remove event listeners correctly
- Clear timers and intervals
- Use WeakMap for caching
- Profile memory with Chrome DevTools
- Identify and fix memory leaks
🚀 Next Steps
Section titled “🚀 Next Steps”- Performance Optimization - Optimize your applications
- Bundle Optimization - Reduce bundle sizes
- Micro Frontends - Scale your architecture
Pro Tip: Memory leaks are silent killers! Use Chrome DevTools Memory Profiler regularly to catch leaks early. Always clean up subscriptions, event listeners, and timers in ngOnDestroy! 🧠