Ответ
Сборщик мусора (GC) в CPython использует два основных механизма для освобождения памяти:
-
Подсчет ссылок (Reference Counting) Это основной и самый быстрый механизм. У каждого объекта есть счетчик, который отслеживает, сколько переменных на него ссылается. Когда счетчик достигает нуля, память из-под объекта освобождается немедленно.
import sys a = [] # Создан объект list, счетчик ссылок = 1 b = a # Счетчик ссылок на тот же объект = 2 print(sys.getrefcount(a)) # Выведет 3 (a, b и временная ссылка в getrefcount) del a del b # Счетчик ссылок стал 0, объект удаляется -
Обнаружение циклических ссылок (Generational GC) Подсчет ссылок не работает, если объекты ссылаются друг на друга, образуя цикл. Для таких случаев GC периодически запускает алгоритм, который находит "острова" изолированных объектов (недостижимых из основной программы) и удаляет их.
Пример циклической ссылки:
import gc class Node: def __init__(self, name): self.name = name self.neighbor = None # Создаем два объекта, которые ссылаются друг на друга node1 = Node('A') node2 = Node('B') node1.neighbor = node2 node2.neighbor = node1 # Цикл: node1 -> node2 -> node1 # Удаляем внешние ссылки на объекты del node1 del node2 # Объекты все еще в памяти, т.к. их счетчики ссылок не равны 0. # Они будут удалены при следующем запуске сборщика мусора. gc.collect() # Принудительный запуск для демонстрацииТаким образом, большинство объектов удаляется мгновенно, а циклические зависимости очищаются периодически.
Ответ 18+ 🔞
Так, слушай, про сборщик мусора в питоне — это же классика, блядь! Как будто про Герасима и Муму, только про память. Сейчас разжую, чтобы понятно было, ебать мои старые костыли.
Вот представь: CPython, этот наш старый добрый зверь, для уборки памяти использует две основные хитрости. Одна — быстрая, как удар током, вторая — хитрая, для особо запущенных случаев.
Первая — подсчет ссылок (Reference Counting). Это как будто у каждого объекта в голове висит маленький счетчик, который тикает: «На меня смотрят? На меня смотрят?». Каждая новая переменная, которая на него показывает — плюс один. Переменную удалили — минус один. Как только счетчик падает до нуля — объект тут же, нахуй, в небытие, память свободна. Мгновенно, без разговоров!
import sys
a = [] # Родился список, счетчик = 1 (смотрит 'a')
b = a # Теперь на него смотрят 'a' И 'b', счетчик = 2
print(sys.getrefcount(a)) # Выведет 3, потому что функция тоже на него временно посмотрит
del a # Минус один зритель
del b # Минус второй, счетчик НОЛЬ — объект приказал долго жить, память очищена.
Красота, да? Прямота, ясность. Но, ёпта, есть же подлянка!
Вторая — для циклических ссылок (Generational GC). Вот это уже драма, блядь, уровня «Муму». Подсчет ссылок тут бессилен. Представь: два объекта схватились друг за друга мертвой хваткой и не отпускают. Со стороны на них уже никто не смотрит, но они-то друг на друга смотрят! Счетчики у каждого — единица. И всё, тупик. Они в памяти навечно, как призраки.
Для таких упоротых случаев есть отдельный, более тяжелый сборщик. Он просыпается периодически, как сонный дворник, и начинает шарить по закоулкам памяти. Находит эти самые «острова» изолированных упырей, которые друг на друга ссылаются, но от основного кода уже отрезаны, и — бац! — отправляет их к праотцам.
Вот тебе живой пример, как эту петлю создать:
import gc
class Node:
def __init__(self, name):
self.name = name
self.neighbor = None # Пока соседа нет
# Создаем двух товарищей
node1 = Node('Вася')
node2 = Node('Петя')
# И теперь делаем их неразлучными, блядь
node1.neighbor = node2 # Вася смотрит на Петю
node2.neighbor = node1 # Петя смотрит на Васю. ЦИКЛ, ёпта!
# А теперь мы, как злая барыня, внешние ссылки обрубаем
del node1
del node2
# Ну вроде бы и нет их? Ан нет! Они же друг на друга в упор пялятся!
# Счетчики по 1, подсчет ссылок их не возьмет.
# Вот тут наш терпеливый сборщик (Generational GC) их и приберет.
# Можешь даже пнуть его принудительно для чистоты эксперимента:
gc.collect() # И нет больше нашей вечной дружбы. Пиздец циклам.
Вот и вся магия, блядь. Обычный мусор убирается сразу, по-честному. А эти хитрожопые цикличные зависимости — ну, подождут немного, пока большой уборщик с метлой не придет. Всё гениальное просто, как три рубля.