Логотип YeaHub

База вопросов

Собеседования

Тренажёр

База ресурсов

Обучение

Навыки

Войти

Выбери, каким будет IT завтра — вместе c нами!

YeaHub — это полностью открытый проект, призванный объединить и улучшить IT-сферу. Наш исходный код доступен для просмотра на GitHub. Дизайн проекта также открыт для ознакомления в Figma.

© 2026 YeaHub

Документы

Медиа

Назад
Вопрос про JavaScript: garbage collection, circular reference, memory leak, reference counting, mark and sweep

Как работает сборщик мусора при циклических зависимостях?

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

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

Сборщик мусора (GC) автоматически освобождает память, занятую объектами, на которые больше нет ссылок из активной части программы. При циклических зависимостях два или более объекта ссылаются друг на друга, но на всю группу извне может не быть ссылок. Простые алгоритмы, основанные только на подсчёте ссылок, не смогут удалить такие циклы, что приводит к утечке памяти. Современные GC (например, в JavaScript или Java) используют алгоритмы вроде "Mark-and-Sweep", которые, начиная с корневых объектов (глобальные переменные, стек вызовов), помечают все достижимые объекты, а затем удаляют всё непомеченное, что позволяет безопасно собирать недостижимые циклы.

Длинный ответ

Сборка мусора — это автоматический процесс управления памятью, который освобождает ресурсы, занятые объектами, ставшими ненужными программе. Ключевая проблема возникает, когда объекты образуют замкнутый цикл ссылок, но ни один из них не доступен из "корней" программы (например, из глобальной области видимости или стека вызовов).

Проблема циклических ссылок

Рассмотрим простой пример на псевдокоде: объект A содержит ссылку на объект B, а объект B содержит ссылку обратно на объект A. Если внешняя ссылка на A или B удаляется, оба объекта становятся недостижимыми для программы, но всё ещё ссылаются друг на друга. Алгоритм, основанный исключительно на подсчёте ссылок, увидит, что счётчик ссылок у каждого объекта равен 1 (исходящий из другого объекта цикла), и не освободит память, что является классической утечкой.

Современные алгоритмы GC

Большинство современных сред выполнения (JVM для Java, V8 для JavaScript, .NET CLR) используют алгоритмы, которые определяют достижимость объектов, а не просто подсчитывают ссылки. Наиболее распространённый подход — Mark-and-Sweep ("Пометить и очистить"):

  1. Фаза пометки (Mark): GC начинает обход от набора корневых объектов (root set) — это глобальные переменные, локальные переменные в активных вызовах функций, статические поля и т.д. Все объекты, до которых можно добраться из корней, помечаются как живые.
  2. Фаза очистки (Sweep): Память сканируется, и все непомеченные объекты считаются мусором и освобождаются. Недостижимые циклические структуры не будут помечены на фазе Mark, так как к ним нет пути из корней, поэтому они будут корректно удалены на фазе Sweep.

Пример на JavaScript

JavaScript (в движках типа V8) использует усовершенствованный сборщик мусора, который эффективно обрабатывает циклы. Рассмотрим код, создающий циклическую ссылку:

function createCycle() {
  let objA = { name: 'A' };
  let objB = { name: 'B' };
  // Создаём циклическую ссылку
  objA.ref = objB;
  objB.ref = objA;
  // После выхода из функции локальные переменные objA и objB
  // перестают существовать, но объекты ссылаются друг на друга.
  // Однако GC (Mark-and-Sweep) поймёт, что они недостижимы из глобальной области,
  // и удалит оба.
}
createCycle();
// Здесь GC может безопасно собрать objA и objB.

После выполнения функции createCycle локальные переменные objA и objB выходят из области видимости. Хотя объекты ссылаются друг на друга, на них больше нет ссылок из корневого набора (глобальных переменных или стека). Поэтому на фазе пометки они не будут помечены как живые и впоследствии будут освобождены.

Где это применяется

Понимание работы GC с циклами важно при разработке на языках с автоматическим управлением памятью (Java, C#, Python, JavaScript, Go) для написания надёжных приложений. Особенно это критично в долгоживущих процессах (серверные приложения, мобильные приложения), где накопление неудаляемых циклических ссылок могло бы привести к постепенному исчерпанию памяти. Разработчику обычно не нужно вручную разрывать циклы, но следует избегать создания глобальных или долгоживущих ссылок на объекты, которые могут участвовать в циклах, если эти объекты должны быть собраны.

Вывод: Современные сборщики мусора, использующие алгоритмы определения достижимости (как Mark-and-Sweep), корректно обрабатывают циклические зависимости, автоматически освобождая память недостижимых циклов. Это избавляет разработчика от ручного управления памятью в большинстве сценариев, но требует понимания, какие ссылки удерживают объекты в памяти, чтобы не создавать непреднамеренные долгоживущие ссылки.

Уровень

  • Рейтинг:

    3

  • Сложность:

    5

Навыки

  • JavaScript

    JavaScript

  • Node.js

    Node.js

Ключевые слова

#garbage collection

#circular reference

#memory leak

#reference counting

#mark and sweep

Подпишись на Java Developer в телеграм