Что такое сборка мусора
Что проверяют
Знаете ли, что в CPython работают два разных механизма, и понимаете ли, какой из них когда срабатывает. Тема всплывает, когда в проде растёт память, и от разработчика ждут диагностики, а не общих слов про автоматическое управление.
Как отвечать
Основной механизм это подсчёт ссылок. У каждого объекта есть счётчик, он меняется при создании и удалении ссылок, и при обнулении объект освобождается сразу же, детерминированно. На этом в CPython работает подавляющее большинство освобождений.
Счётчик не справляется с циклами: два объекта ссылаются друг на друга, снаружи ссылок нет, а счётчики не нули. Для них существует отдельный поколенческий сборщик из модуля gc. Новые объекты попадают в младшее поколение, пережившие сборку переходят в старшее, запуск происходит по разнице числа созданных и уничтоженных объектов. Важная деталь: gc.disable() выключает только цикловой сборщик, подсчёт ссылок работает всегда.
Что полезно добавить из практики: принудительный gc.collect() на горячем пути стоит дорого; перед fork помогает gc.freeze(), чтобы долгоживущие объекты не заставляли копировать страницы при первой же сборке в рабочих процессах; циклы разрывают слабыми ссылками; для поиска роста памяти есть tracemalloc и обход объектов через gc.get_objects(). Освобождённая память не всегда возвращается операционной системе: аллокатор держит свои арены.
Где спотыкаются
Считают, что del удаляет объект. Он удаляет имя и уменьшает счётчик, а объект живёт, пока на него ссылается хоть кто-то: список, замыкание, кэш, стек трассировки исключения, ссылка на который держится дольше, чем ожидается.
Повторяют старое утверждение, что объекты в циклах с определённым методом финализации не собираются вовсе. В современных версиях CPython такие циклы собираются, а вот неубиваемым делает объект живая ссылка из глобального кэша, и искать надо её.
Переносят поведение CPython на все реализации. В PyPy подсчёта ссылок нет, освобождение отложено, и код, который полагался на закрытие файла в момент выхода из области видимости, начинает держать дескрипторы. Поэтому файлы и соединения закрывают контекстным менеджером, а не расчётом на счётчик.