Утечки памяти в Angular возникают, когда ресурсы (объекты, подписки и т.д.) не освобождаются после уничтожения компонента или сервиса. Вот основные методы предотвращения утечек:
Самая частая причина утечек — незакрытые подписки на RxJS Observables.
// Плохо: подписка не отменяется
this.someService.data$.subscribe(data => {...});
// Хорошо: ручная отписка
private subscription: Subscription;
ngOnInit() {
this.subscription = this.someService.data$.subscribe(data => {...});
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
// Лучше: использование takeUntil
private destroy$ = new Subject<void>();
ngOnInit() {
this.someService.data$
.pipe(takeUntil(this.destroy$))
.subscribe(data => {...});
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
Все setTimeout/setInterval должны быть очищены:
private timerId: any;
ngOnInit() {
this.timerId = setInterval(() => {...}, 1000);
}
ngOnDestroy() {
clearInterval(this.timerId);
}
Слушатели событий DOM должны быть удалены:
@HostListener('window:scroll', ['$event'])
onScroll(event: Event) {...}
// Или для ручного добавления:
private removeListener: () => void;
ngOnInit() {
this.removeListener = this.renderer.listen('document', 'click', () => {...});
}
ngOnDestroy() {
this.removeListener();
}
Сервисы, предоставленные в root, живут всё время приложения. Для временных данных используйте:
Все Subject должны быть завершены:
private dataSubject = new Subject<string>();
ngOnDestroy() {
this.dataSubject.complete();
}
В шаблонах предпочтительно использовать async pipe, который автоматически управляет подпиской:
<div>{{ data$ | async }}</div>
Удаляйте ссылки на большие объекты:
private largeData: any;
ngOnDestroy() {
this.largeData = null;
}
Стратегия OnPush уменьшает количество проверок изменений и связанных с ними утечек.
основные правила — всегда отписываться от Observables, очищать таймеры и слушатели событий, завершать Subject'ы и избегать циклических ссылок. Использование async pipe и OnPush стратегии значительно снижает риск утечек.