Что такое счётчик ссылок в CPython и как он работает?

Ответ

Счётчик ссылок (reference counting) — это основной механизм управления памятью в CPython. Его суть в том, что для каждого объекта в памяти хранится счётчик, который отслеживает, сколько переменных и структур данных на него ссылаются.

Принцип работы:

  1. Создание ссылки: Когда на объект создаётся новая ссылка (например, присваивание переменной), его счётчик увеличивается на 1.
  2. Удаление ссылки: Когда ссылка на объект удаляется (например, переменная выходит из области видимости или ей присваивается None), счётчик уменьшается на 1.
  3. Освобождение памяти: Как только счётчик достигает нуля, это означает, что на объект больше нет ссылок, и сборщик мусора немедленно освобождает занимаемую им память.

Пример:

import sys

# 1. Создаем объект, на него ссылается 'a'. Счётчик = 1.
my_list = [1, 2, 3]
print(f"Ref count after creation: {sys.getrefcount(my_list)}")

# 2. Создаем новую ссылку 'b'. Счётчик = 2.
b = my_list
print(f"Ref count after new reference: {sys.getrefcount(my_list)}")

# 3. Удаляем ссылку 'b'. Счётчик = 1.
del b
print(f"Ref count after deleting one reference: {sys.getrefcount(my_list)}")

# Примечание: sys.getrefcount() сама создает временную ссылку,
# поэтому возвращаемое значение всегда на 1 больше реального.

Ограничения: циклические ссылки

Счётчик ссылок не может обработать циклические ссылки, когда два или более объектов ссылаются друг на друга. В этом случае их счётчики никогда не достигнут нуля, даже если на них нет внешних ссылок.

# a ссылается на b, а b ссылается на a
a = []
b = [a]
a.append(b)

# Даже после удаления внешних ссылок, они продолжают
# ссылаться друг на друга, и память не освобождается
del a
del b

Для решения этой проблемы в Python есть дополнительный механизм — поколенческий сборщик мусора (Generational Garbage Collector), который периодически находит и удаляет такие "осиротевшие" циклические группы объектов.