> Как сборщик мусора находит неиспользуемые объекты в JavaScript (JavaScript)

Уровень: middle · Роль: frontend · Категория: Технические вопросы

Компании: ООО Снэп АйТи

Стек: JavaScript

> Пример ответа

Короткий ответ

Сборщик мусора в JavaScript находит неиспользуемые объекты через алгоритм mark-and-sweep (пометка и зачистка). Он стартует от корневых ссылок (глобальный объект, текущий стек вызовов, DOM-дерево) и рекурсивно помечает все достижимые объекты. Затем удаляет всё, что не помечено. Дополнительно применяются оптимизации: generational collection, incremental marking и idle-time collection. Главный критерий - достижимость объекта из корней, а не явное освобождение памяти.

Подробное объяснение

Современные движки (V8, SpiderMonkey, JavaScriptCore) используют tracing garbage collector с алгоритмом mark-and-sweep. Процесс состоит из трёх фаз:

  1. Mark - сборщик строит граф объектов, начиная с корней:

    • глобальный объект (globalThis в Node.js, window в браузере)
    • текущие вызовы функций (call stack)
    • активные замыкания и локальные переменные
    • DOM-элементы, привязанные к документу
    • зарегистрированные обработчики событий

    Каждый достижимый объект помечается специальным битом.

  2. Sweep - движок проходит по всей куче и освобождает память объектов без метки.

  3. Compact (необязательно) - перемещает выжившие объекты для уменьшения фрагментации.

Ключевое отличие от reference counting (использовался в старых браузерах) - отсутствие проблемы циклических ссылок. Два объекта, ссылающиеся друг на друга, но недостижимые из корней, будут корректно удалены.

Дополнительные механизмы:

  • Generational collection - объекты делятся на молодое и старое поколения. Молодые проверяются чаще, так как большинство объектов умирают быстро.
  • Incremental marking - разбивает mark-фазу на небольшие порции, чтобы не блокировать main thread.
  • Idle-time collection - сборка запускается в периоды простоя браузера.

На практике

Для разработчика важно понимать, что сборщик мусора не гарантирует немедленного освобождения памяти после потери последней ссылки. Время сборки зависит от давления на память и политики движка.

Практические следствия:

  • Утечки памяти возникают, когда объект остаётся достижимым, хотя больше не нужен. Типичные причины: забытые таймеры, слушатели событий, глобальные переменные, кэши без ограничений.
  • WeakMap и WeakSet позволяют хранить ссылки на объекты, не препятствуя их сборке. Это полезно для кэширования и мемоизации.
  • Performance - создание большого количества короткоживущих объектов увеличивает частоту minor GC, что может вызывать микро-фризы.

Пример кода

JAVASCRIPT
// Утечка через глобальную переменную
let cache = [];
function addToCache(data) {
cache.push(data); // объект остаётся достижимым через cache
}
// Правильный подход с WeakMap
const weakCache = new WeakMap();
function cacheResult(key, value) {
weakCache.set(key, value); // если key соберётся, value тоже
}
// Утечка через таймер
function startTimer() {
const heavyObject = { data: new Array(1000000) };
setInterval(() => {
console.log(heavyObject.data.length); // heavyObject живёт вечно
}, 1000);
}
// Правильно - очистить таймер
let timerId;
function startTimerFixed() {
const heavyObject = { data: new Array(1000000) };
timerId = setInterval(() => {
console.log(heavyObject.data.length);
}, 1000);
}
function stopTimer() {
clearInterval(timerId);
}

Как отвечать на собеседовании

Начни с краткого определения: "Сборщик мусора использует алгоритм mark-and-sweep, основанный на достижимости объектов из корневых ссылок". Затем опиши фазы: mark, sweep, compact. Упомяни, что современные движки добавляют generational collection и incremental marking.

Подчеркни практические аспекты: почему циклические ссылки не проблема, как WeakMap помогает избежать утечек, какие типичные паттерны приводят к удержанию памяти. Если спросят про конкретный движок - расскажи про V8 и его механизмы (Scavenger для молодого поколения, Mark-Compact для старого).

Хорошо показать понимание trade-off: сборщик мусора упрощает разработку, но требует контроля над временем жизни объектов в долгоживущих приложениях.

Что проверяет интервьюер

Интервьюер оценивает:

  • Понимание базового алгоритма, а не заученных терминов
  • Умение объяснить, почему reference counting не работает с циклами
  • Знание практических последствий для кода (утечки, WeakMap)
  • Осведомлённость о современных оптимизациях движков
  • Способность связать теорию с реальными сценариями (SPA, обработчики событий, кэширование)

Для middle-уровня достаточно уверенного объяснения mark-and-sweep и типичных утечек. Для senior ожидается глубокое знание внутренностей V8 и умение диагностировать проблемы памяти через DevTools.

Типичные ошибки

  • Утверждение, что JavaScript использует reference counting - это было в старых браузерах (IE6-7), современные движки используют tracing GC.
  • Путаница между достижимостью и количеством ссылок - объект с одной ссылкой из корня живёт, объект с тысячей ссылок, но без корней - мёртв.
  • Ожидание немедленного освобождения памяти - GC работает асинхронно и по своим правилам.
  • Игнорирование утечек через замыкания - замыкание, захватившее большой объект, может держать его в памяти, пока живо само замыкание.
  • Непонимание WeakMap - некоторые считают, что WeakMap хранит ключи, но на самом деле он хранит слабые ссылки на ключи, а значения - сильные.
  • Утверждение, что delete или null гарантируют сборку - это лишь убирает ссылку, но не управляет моментом сборки.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью