Ответ
В 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).
- Процесс:
- Новые объекты попадают в поколение 0.
- Сборщик запускается чаще всего для самого молодого поколения.
- Он находит "недостижимые" объекты (включая циклические ссылки) с помощью алгоритма
mark-and-sweep(пометить и убрать). - Объекты, которые "выжили" после нескольких сборок в младшем поколении, перемещаются в старшее (из 0 в 1, из 1 в 2).
Управлять сборщиком можно с помощью модуля gc:
import gc
# Проверить, включен ли GC
print(gc.isenabled())
# Принудительно запустить сборку мусора
gc.collect()