Memory leaks и отписка от потоков

Одной из наиболее частых причин утечек памяти в Angular являются незавершённые подписки на Observable потоки, такие как HttpClient, EventEmitter или сторонние библиотеки.

Причины утечек памяти

  1. Долгоживущие подписки — Observable не завершает работу самостоятельно (interval, fromEvent).
  2. Неявные подписки — подписка внутри сервисов или компонентов без последующей отписки.
  3. Динамическое создание компонентов — если подписки создаются при каждом рендере, старые объекты остаются в памяти.

Методы безопасной отписки

  1. ngOnDestroy и ручная отписка
export class ExampleComponent implements OnDestroy {
  private subscription: Subscription;

  constructor(private service: DataService) {
    this.subscription = this.service.getData().subscribe(data => console.log(data));
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
}
  1. takeUntil с Subject
private destroy$ = new Subject<void>();

ngOnInit() {
  this.service.getData()
    .pipe(takeUntil(this.destroy$))
    .subscribe(data => console.log(data));
}

ngOnDestroy() {
  this.destroy$.next();
  this.destroy$.complete();
}
  1. Async Pipe

Использование async в шаблоне автоматически управляет подпиской и отпиской:

<div *ngIf="data$ | async as data">{{ data }}</div>
  1. Subscription.add для нескольких подписок
private subscriptions = new Subscription();

ngOnInit() {
  this.subscriptions.add(this.service.getData().subscribe());
  this.subscriptions.add(this.service.getOtherData().subscribe());
}

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

Контроль утечек

  • Использовать инструменты браузера (Chrome DevTools) для анализа памяти.
  • Проверять количество живых компонентов и подписок после навигации между страницами.
  • Избегать подписки внутри конструктора, отдавая предпочтение ngOnInit.

Соблюдение этих практик позволяет поддерживать производительность приложений, предотвращать перегрузку памяти и обеспечивать корректную работу Angular-приложений при длительной эксплуатации.