Память и сборщик мусора, утечки
↑ 2.1 JavaScript: продвинутый · ← Предыдущая · → Следующая
Зачем это на собесе
Утечки памяти в SPA — частая проблема; ждут перечня причин и способов найти.
Объяснение
Память управляется автоматически. Алгоритм GC — mark-and-sweep: от корней (глобальный объект, стек, активные замыкания) помечаются достижимые объекты; остальные удаляются. Циклические ссылки не мешают освобождению. Современный V8 — поколенческий (young/old), инкрементальный и параллельный GC.
Утечка — объекты, ставшие ненужными, но остающиеся достижимыми.
| Причина | Пример | Решение |
|---|---|---|
| Глобальные переменные | случайное присваивание без объявления | strict mode, линтер |
| Забытые таймеры | setInterval без clearInterval | очистка в unmounted |
| Слушатели событий | addEventListener без снятия; слушатели на window/document | removeEventListener, AbortController |
| Замыкания | удерживают большие объекты | обнулять ссылки, не захватывать лишнее |
| Отсоединённые DOM-узлы | ссылка в JS на удалённый элемент | null, WeakRef/WeakMap |
| Неограниченные кэши, стор | накопление без вытеснения | LRU, лимиты, TTL |
| Подписки (RxJS, WebSocket, EventBus) | не отписались | отписка в onUnmounted |
| Сторонние библиотеки | инстансы карт/графиков не уничтожены | destroy() |
// Vue
onMounted(() => { timer = setInterval(tick, 1000); window.addEventListener("resize", onResize); });
onUnmounted(() => { clearInterval(timer); window.removeEventListener("resize", onResize); });
const ctrl = new AbortController();
window.addEventListener("scroll", onScroll, { signal: ctrl.signal }); // ctrl.abort() снимаетДиагностика (Chrome DevTools → Memory): снимки кучи (Heap snapshot) до/после действия и сравнение (Comparison), «Allocation instrumentation on timeline», вкладка Performance → флажок Memory, поиск Detached DOM nodes. Признак утечки: пилообразный график, не возвращающийся к базовой линии.
Нюансы и подводные камни
WeakRefиFinalizationRegistryне гарантируют время очистки; используйте редко.- Большие
ArrayBuffer/Blobосвобождаются не сразу:URL.revokeObjectURL. - SPA живёт часами: небольшая утечка накапливается.
- Утечки часто связаны с компонентами, которые не размонтируются (кэш
KeepAlive).
Практика
- Воспроизведите утечку через таймер и найдите её в Heap snapshot.
- Сравните два снимка после открытия/закрытия модального окна 20 раз.
- Добавьте очистку в
onUnmountedдля всех подписок.
Вопросы с ответами
Как работает сборщик мусора?
Помечает достижимые от корней объекты и удаляет недостижимые (mark-and-sweep).
Какие бывают утечки в SPA?
Таймеры, слушатели, замыкания, отсоединённые DOM-узлы, неограниченные кэши, забытые подписки.