← Назад к вопросам
Когда удаляется объект в сборщике мусора (Garbage collector)?
2.0 Middle🔥 141 комментариев
#Python Core
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Когда объект удаляется в сборщике мусора Python
В Python используется двухуровневая система управления памятью:
- Reference counting (подсчёт ссылок) — основной механизм
- Garbage collector (сборщик мусора) — для циклических ссылок
Уровень 1: Reference Counting (счётчик ссылок)
Это основной механизм удаления в Python:
import sys
obj = [] # Создан объект, счётчик = 1
print(sys.getrefcount(obj)) # 2 (сам obj + аргумент getrefcount)
ref1 = obj # Счётчик = 2
print(sys.getrefcount(obj)) # 3
ref2 = obj # Счётчик = 3
print(sys.getrefcount(obj)) # 4
del ref1 # Счётчик = 3
print(sys.getrefcount(obj)) # 3
del ref2 # Счётчик = 2
print(sys.getrefcount(obj)) # 2
del obj # Счётчик = 0, объект УДАЛЁН СРАЗУ
# __del__ вызывается здесь
Объект удаляется СРАЗУ когда ref count = 0, не ждёт сборщика мусора.
Уровень 2: Garbage Collector (для циклических ссылок)
Когда объекты ссылаются друг на друга, ref counting не помогает:
import gc
class Node:
def __init__(self, name):
self.name = name
self.next = None
def __del__(self):
print(f"{self.name} удалён")
# Циклическая ссылка
a = Node("A")
b = Node("B")
a.next = b # a -> b
b.next = a # b -> a (цикл!)
print(f"Ref count A: {sys.getrefcount(a)}") # > 1
print(f"Ref count B: {sys.getrefcount(b)}") # > 1
del a # А НЕ удаляется! Есть цикл
del b # B НЕ удаляется! Есть цикл
print("\nОбъекты ещё в памяти")
# Сборщик мусора находит циклы
gc.collect() # Теперь удаляет A и B
print("Мусор собран")
# Вывод:
# A удалён
# B удалён
Когда вызывается сборщик мусора
1. Автоматически по поколениям
import gc
# Просмотр поколений
print(gc.get_threshold()) # (700, 10, 10)
# Поколение 0: 700 объектов -> gc.collect()
# Поколение 1: 10 сборок поколения 0 -> gc.collect()
# Поколение 2: 10 сборок поколения 1 -> gc.collect()
# Счётчики текущего состояния
print(gc.get_count()) # (объектов в поколении 0, 1, 2)
2. Явно вызываем gc.collect()
import gc
# Принудительно собрать мусор
collected = gc.collect()
print(f"Собрано объектов: {collected}")
3. При отключении сборщика
import gc
# Выключить автоматическую сборку
gc.disable()
# ... код работает быстрее, но память растёт
# Периодически собираем вручную
gc.collect()
# Включить обратно
gc.enable()
Практический пример с del
import gc
import sys
class Resource:
def __init__(self, name):
self.name = name
print(f"{self.name}: __init__")
def __del__(self):
print(f"{self.name}: __del__ вызван")
# Сценарий 1: Простое удаление (ref count = 0)
print("--- Сценарий 1: простое удаление ---")
res1 = Resource("res1") # создан
del res1 # __del__ вызван СРАЗУ
# Вывод:
# res1: __init__
# res1: __del__ вызван
print("\n--- Сценарий 2: циклическая ссылка ---")
class CircularResource:
def __init__(self, name):
self.name = name
self.self_ref = None
print(f"{self.name}: __init__")
def __del__(self):
print(f"{self.name}: __del__ вызван")
res2 = CircularResource("res2")
res2.self_ref = res2 # цикл!
del res2 # __del__ НЕ вызывается сразу
print("res2 удаляется после gc.collect()")
gc.collect() # Теперь удаляется
# Вывод:
# res2: __init__
# res2: удаляется после gc.collect()
# res2: __del__ вызван
Проверка объектов в памяти
import gc
import sys
print("--- Анализ памяти ---")
# Получить все объекты
all_objects = gc.get_objects()
print(f"Всего объектов в памяти: {len(all_objects)}")
# Объекты с циклическими ссылками
gc.collect()
garbages = gc.garbage # Только если DEBUG флаг включен
print(f"Необработанный мусор: {len(garbages)}")
# Отслеживание утечек памяти
def find_circular_refs():
gc.set_debug(gc.DEBUG_SAVEALL)
gc.collect()
for obj in gc.garbage:
print(type(obj), id(obj))
gc.set_debug(0)
gc.garbage.clear()
find_circular_refs()
Важные моменты
1. Context managers для гарантированной очистки
# Плохо: полагаться на __del__
file = open("data.txt")
data = file.read()
# __del__ может не вызваться долго
# Хорошо: гарантированная очистка
with open("data.txt") as file:
data = file.read()
# __exit__ вызывается сразу
2. weakref для избежания циклов
import weakref
class Parent:
def __init__(self, name):
self.name = name
self.children = []
class Child:
def __init__(self, name, parent):
self.name = name
self.parent = weakref.ref(parent) # слабая ссылка!
parent = Parent("parent")
child = Child("child", parent)
del parent # Удаляется, нет цикла
del child
3. Отключение GC для оптимизации
import gc
gc.disable() # Отключить сборщик
# Код где нет циклических ссылок
for i in range(1_000_000):
obj = {"data": i}
# obj удаляется по ref count
gc.enable()
Резюме
| Ситуация | Когда удаляется | Как удаляется |
|---|---|---|
| Простой объект, ref count = 0 | СРАЗУ | Reference counting |
| Циклические ссылки | При gc.collect() | Garbage collector |
После del | Если ref count = 0 | Reference counting |
| В контексте with | При выходе из блока | __exit__ |
| Неиспользуемые переменные | В конце scope | Reference counting |
Золотое правило: В Python ref counting удаляет объекты почти сразу, а gc.collect() ловит циклические ссылки.