gc), который периодически сканирует объекты, строит граф ссылок и находит группы взаимно ссылающихся объектов, недостижимых из корней — именно они считаются мусором и освобождаются.
Как работает GC с циклическими ссылками?
Подробный ответ
Проблема циклических ссылок при подсчёте ссылок
В CPython у каждого объекта есть счётчик ссылок; когда он падает до нуля, объект немедленно деаллоцируется. Однако если два или больше объектов ссылаются друг на друга, их счётчики никогда не опустятся до нуля, даже если на этот цикл больше нельзя добраться из программы.
a = {}
b = {}
a["b"] = b
b["a"] = a
del a, b # внешних ссылок больше нет, но объекты держат друг другаС точки зрения простого reference counting оба словаря «живы» бесконечно. Чтобы такие циклы не накапливались, поверх подсчёта ссылок работает дополнительный сборщик мусора.
Генерационный GC и поиск циклов
GC в CPython специализируется на обнаружении циклических ссылок. Он делит объекты на поколения (0 — молодые, 1 и 2 — более старые) и периодически запускает сборку:
младшее поколение сканируется чаще, так как там много короткоживущих объектов;
старшие поколения — реже, чтобы не тратить ресурсы на долгоживущие объекты.
Алгоритм упрощённо выглядит так:
выбрать множество кандидатов на сборку (обычно контейнеры: списки, словари, пользовательские объекты), у которых могут быть циклы;
построить граф ссылок между ними;
определить, какие объекты достижимы из «корней» (глобальные переменные, стеки вызовов, регистрированные объекты и т.п.);
те объекты, которые достижимы только через друг друга, но не от корней, считаются мусором и могут быть освобождены, даже если их счётчики ссылок > 0.
Роль модуля gc
Модуль gc даёт управление над этим механизмом:
gc.get_threshold()/gc.set_threshold()— пороги, при превышении которых запускается сборка поколений;gc.collect()— принудительная сборка, возвращает количество собранных объектов;gc.get_objects(),gc.get_stats()— диагностика и отладка утечек;gc.garbage— список объектов, которые не удалось корректно финализировать (например, из‑за__del__в цикле).
Циклы и __del__
Особый случай — циклы, в которые вовлечены объекты с определённым __del__. Уничтожать такие объекты сложно: порядок вызова финализаторов может быть неопределённым, а внутри __del__ могут быть побочные эффекты. Поэтому GC может не собирать такие циклы автоматически, перекладывая их в gc.garbage; этим нужно управлять вручную.
Формулировка уровня senior
В CPython основной механизм управления памятью — подсчёт ссылок, который не умеет сам по себе собирать циклы. Поверх него работает генерационный GC, который периодически сканирует контейнеры, строит граф ссылок и вычищает группы объектов, достижимые только друг через друга. Через модуль gc можно настраивать пороги, принудительно запускать сборку и диагностировать «висящие» объекты, особенно когда в циклах участвуют объекты с __del__.
Оцени свой прогресс