ПрограммированиеBackend разработчик

Объясните, как работает управление памятью в Python, включая сборщик мусора. Что такое циклические ссылки, и как Python с ними борется?

Проходите собеседования с ИИ помощником Hintsage

Ответ

В Python память управляется автоматически: при создании объекта память выделяется, а когда на объект больше не ссылается ни один другой объект, память освобождается. Основная стратегия — подсчёт ссылок: к каждому объекту привязан счетчик ссылок, когда он становится нулём — объект удаляется.

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

Для борьбы с этим в Python (CPython) есть сборщик мусора (garbage collector, GC), который обнаруживает циклы объектов, недоступных из корней, и удаляет их. Сборщик мусора можно управлять из модуля gc.

Пример цикла:

class Node: def __init__(self): self.ref = None n1 = Node() n2 = Node() n1.ref = n2 n2.ref = n1 del n1, n2 # объекты всё ещё живы из-за циклической ссылки!

Вопрос с подвохом

Вопрос: «Если вызвать del obj, память всегда освободится немедленно. Правда ли это?»

Ответ: Нет! Операция del лишь убирает одну ссылку на объект. Если где-то (явно или циклично) остаются другие ссылки — память не освободится до тех пор, пока счётчик ссылок не станет равен нулю и/или GC не избавится от циклов. Пример:

import gc class A: pass x = A() y = x del x # у y осталась ссылка, объект жив! del y # только теперь объект может быть удалён

Примеры реальных ошибок из-за незнания тонкостей темы


История

В системе обработки больших файлов держали в памяти связанные объекты. После удаления внешних ссылок память всё равно не высвобождалась. Причина — циклические ссылки, которые не сразу очищались, а иногда и вовсе не очищались из-за наличия деструктора __del__.


История

В долгоживущем серверном процессе часто создавались большие временные структуры с циклическими графами. Разработчики не отключили временный вывод отладки GC, из-за этого были редкие, но ощутимые паузы на фоне. Диагностировали как "просадки производительности". Оказалось, что виноват частый запуск сборщика мусора.


История

При портировании крупного проекта на PyPy (альтернативный интерпретатор) старый код полагался на специфическое поведение CPython по моментальной очистке памяти через счетчик ссылок — в PyPy объекты удалялись и очищались не сразу, а по "желанию" GC, что привело к непредсказуемому поведению с открытыми файлами и сетевыми соединениями.