Сборщик мусора и циклические ссылки
Узнайте, как модуль gc обрабатывает циклы ссылок.
«Сборщик мусора и циклические ссылки» — бесплатный урок Python Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Python Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Python Academy содержит 4 уроков всего.
Почему подсчёта ссылок недостаточно
Подсчёт ссылок не может собрать объекты, участвующие в циклах ссылок: каждый объект хранит ссылку на другой, поэтому ни один счётчик не уменьшается до 0.
# Reference cycle: a → b → a
a = {}
b = {"other": a}
a["other"] = b
del a, b
# Both objects are unreachable but refcount > 0
# Only the cyclic GC can collect themМодуль gc
gc — это циклический сборщик мусора. Он периодически сканирует отслеживаемые объекты, находя недоступные циклы, и собирает их.
import gc
gc.collect() # trigger immediately
print(gc.garbage) # objects with __del__ in cycles (cannot auto-collect)Поколенческий сбор
Сборщик мусора использует три поколения. Новые объекты начинают с поколения 0. Объекты, пережившие сборку, переводятся в следующее поколение. Поколение 0 собирается чаще всего.
import gc
print(gc.get_count()) # (gen0, gen1, gen2) allocations since last collection
print(gc.get_threshold()) # (700, 10, 10) — thresholds for each generation
gc.set_threshold(1000, 15, 10) # tuneОбнаружение циклов с помощью gc.get_referents
Проверяйте, на какие объекты ссылается объект: это полезно для понимания того, почему что-то не собирается.
import gc
a = []
b = [a]
a.append(b)
for ref in gc.get_referents(a):
print(ref) # shows bgc.get_objects()
gc.get_objects() возвращает все объекты, которые в данный момент отслеживаются сборщиком мусора. Это полезно для поиска утечек памяти.
import gc
before = len(gc.get_objects())
create_lots_of_objects()
after = len(gc.get_objects())
print(f"Leaked: {after - before} objects")Предотвращение циклов
Проектируйте структуры данных так, чтобы избегать циклов: используйте слабые ссылки для обратных ссылок, идентификаторы вместо прямых ссылок или явно разрывайте циклы до выхода объектов из области видимости.
import weakref
class Node:
def __init__(self, parent):
# weakref avoids cycle:
self.parent = weakref.ref(parent)
class Tree:
def __init__(self):
self.child = Node(self) # no hard cycle__del__ и циклы
Объекты с __del__, участвующие в цикле, не могут быть автоматически собраны. Вместо этого они помещаются в gc.garbage.
import gc
class Leaky:
def __del__(self): pass
a = Leaky()
b = Leaky()
a.other = b
b.other = a
del a, b
gc.collect()
print(gc.garbage) # [Leaky, Leaky] — cannot collectgc.freeze() — Python 3.7+
gc.freeze() замораживает отслеживаемые в данный момент объекты, поэтому они больше никогда не собираются. Это полезно в долгоживущих процессах после запуска, чтобы уменьшить паузы сборщика мусора.
import gc
# After all globals and imports are in place:
gc.freeze() # no GC scanning for these objects ever again
# Useful for: gunicorn pre-fork, background serversОтключение и повторное включение сборщика мусора
Отключайте циклический сборщик мусора в плотных циклах, где Вы уверены в отсутствии циклов, а затем снова включайте его и принудительно запускайте сборку.
import gc
gc.disable()
try:
result = tight_loop_no_cycles()
finally:
gc.enable()
gc.collect()isenabled и isfinalized
Проверяйте состояние сборщика мусора с помощью gc.isenabled() и определяйте, завершается ли обработка объекта, с помощью gc.is_finalized(obj).
import gc
print(gc.isenabled()) # True by default
obj = object()
print(gc.is_finalized(obj)) # False (still alive)Типичные причины утечек памяти
Распространённые причины утечек памяти в Python: глобальные кэши, бесконтрольно увеличивающиеся в размере, неотсоединённые обработчики событий, циклические ссылки с __del__ и расширения на C, из которых утекают ссылки.
# Typical leaks:
global_cache = {} # grows forever — use lru_cache with maxsize
class EventBus:
listeners = []
# Listeners hold references to objects:
# @classmethod
# def on(cls, fn): cls.listeners.append(fn)
# Must call off() to detachБыстрая проверка
Что происходит с объектами, имеющими __del__ и участвующими в цикле ссылок?
Итоги
Циклический сборщик мусора обрабатывает циклы ссылок, с которыми не справляется подсчёт ссылок. Он использует три поколения и переводит выжившие объекты в следующие поколения. Избегайте циклов с помощью слабых ссылок. Объекты с __del__ в циклах попадают в gc.garbage. Используйте gc.freeze() после запуска, чтобы уменьшить паузы сборщика мусора в долгоживущих серверах.
Часто задаваемые вопросы
Урок «Сборщик мусора и циклические ссылки» бесплатный?
Да — полный текст урока «Сборщик мусора и циклические ссылки» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Python Academy, подпишись на CoddyKit PRO. Курс Python Academy содержит 4 уроков всего.
Чему я научусь в уроке «Сборщик мусора и циклические ссылки»?
Узнайте, как модуль gc обрабатывает циклы ссылок. Ты практикуешь Python Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Python Academy?
Предыдущий опыт не требуется. Python Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «Сборщик мусора и циклические ссылки»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Python Academy?
Да. Каждый урок Python Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Подсчёт ссылок в CPython
- Сборщик мусора и циклические ссылки
- Профилирование с cProfile и line_profiler
- Профилирование памяти с tracemalloc