Объясните основные механизмы работы сборщика мусора (GC) в Python

Ответ

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

1. Подсчёт ссылок (Reference Counting)

Это основной и самый быстрый механизм. Каждый объект в Python имеет внутренний счётчик, который отслеживает количество ссылок на него.

  • Когда на объект создается новая ссылка (например, присваивание переменной), его счётчик увеличивается.
  • Когда ссылка удаляется (del, выход из области видимости), счётчик уменьшается.
  • Как только счётчик достигает нуля, объект немедленно удаляется из памяти.
import sys

my_list = [1, 2, 3]  # Создан объект, счетчик ссылок = 1
print(sys.getrefcount(my_list) - 1) # Вывод: 1

another_list = my_list # Новая ссылка, счетчик = 2
print(sys.getrefcount(my_list) - 1) # Вывод: 2

del another_list # Ссылка удалена, счетчик = 1
print(sys.getrefcount(my_list) - 1) # Вывод: 1

del my_list # Последняя ссылка удалена, счетчик = 0, объект удаляется

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

2. Циклический сборщик (Generational GC)

Для решения проблемы циклических ссылок Python использует дополнительный алгоритм — сборщик мусора по поколениям.

  • Принцип: Он основан на гипотезе, что большинство объектов "умирают молодыми". Поэтому все объекты разделены на три поколения (0, 1, 2).
  • Процесс:
    1. Новые объекты попадают в поколение 0.
    2. Сборщик запускается чаще всего для самого молодого поколения.
    3. Он находит "недостижимые" объекты (включая циклические ссылки) с помощью алгоритма mark-and-sweep (пометить и убрать).
    4. Объекты, которые "выжили" после нескольких сборок в младшем поколении, перемещаются в старшее (из 0 в 1, из 1 в 2).

Управлять сборщиком можно с помощью модуля gc:

import gc

# Проверить, включен ли GC
print(gc.isenabled())

# Принудительно запустить сборку мусора
gc.collect()