← Назад к вопросам
Зачем сборщик мусора разделяют на 3 категории?
3.0 Senior🔥 81 комментариев
#Python Core
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Зачем сборщик мусора разделяют на 3 категории
Основная концепция
Сборщик мусора (Garbage Collector) в Python разделён на 3 поколения (поколения 0, 1, 2) для оптимизации производительности. Это основано на гипотезе поколений (generational hypothesis): молодые объекты чаще становятся мусором, чем старые.
Три поколения
┌─────────────────┐
│ Поколение 0 │ <- Новые объекты (сборка часто)
├─────────────────┤
│ Поколение 1 │ <- Объекты среднего возраста (сборка реже)
├─────────────────┤
│ Поколение 2 │ <- Старые объекты (сборка очень редко)
└─────────────────┘
Практическая демонстрация
import gc
import sys
# Получаем объекты в каждом поколении
gen0 = len(gc.get_objects())
# Информация о сборщике мусора
print(gc.get_threshold()) # (700, 10, 10) — пороги для каждого поколения
print(gc.get_count()) # (234, 5, 2) — количество объектов в каждом поколении
# Создаём объект
x = [1, 2, 3]
print(gc.get_count()) # (235, 5, 2) — объект в поколении 0
# Запускаем сборку
gc.collect(generation=0) # Сборка только поколения 0 — быстро
Почему 3 поколения?
1. Оптимизация производительности
# Молодые объекты (поколение 0) часто становятся мусором
def create_temporary_objects():
# Эти объекты живут кратко
temp1 = [1, 2, 3]
temp2 = {"key": "value"}
temp3 = "temporary string"
# Удаляются почти сразу
# Старые объекты (поколение 2) обычно используются долго
global_cache = {} # Живёт всё время программы
Вместо проверки ВСЕ объекты при каждой сборке, Python проверяет часто молодые поколения и редко старые.
2. Уменьшение времени сборки
# Сборка поколения 0 — быстро (сотни объектов)
# Сборка поколения 1 — медленнее (тысячи объектов)
# Сборка поколения 2 — очень медленно (все объекты)
import timeit
import gc
# Время сборки поколения 0
time0 = timeit.timeit(lambda: gc.collect(0), number=100)
print(f"Поколение 0: {time0}ms")
# Время сборки поколения 2
time2 = timeit.timeit(lambda: gc.collect(2), number=100)
print(f"Поколение 2: {time2}ms")
Как работают пороги
import gc
# Пороги (700, 10, 10)
print(gc.get_threshold()) # (700, 10, 10)
# Это означает:
# - При 700 новых объектах → запустить сборку поколения 0
# - После 10 сборок поколения 0 → запустить сборку поколения 1
# - После 10 сборок поколения 1 → запустить сборку поколения 2
# Поколение 0: сборка каждые ~700 объектов
# Поколение 1: сборка каждые 7000 объектов (700 * 10)
# Поколение 2: сборка каждые 70000 объектов (700 * 10 * 10)
Жизненный цикл объекта
import gc
class TrackedObject:
pass
# Отключаем автоматическую сборку для демонстрации
gc.disable()
# Создаём объект — он в поколении 0
obj = TrackedObject()
print(f"Начально: {gc.get_count()}") # Поколение 0 увеличилось
# После сборки поколения 0, объект переходит в поколение 1
gc.collect(generation=0)
print(f"После сборки 0: {gc.get_count()}")
# После сборки поколения 1, объект переходит в поколение 2
gc.collect(generation=1)
print(f"После сборки 1: {gc.get_count()}")
# Объект остаётся в поколении 2 до удаления
gc.enable()
Практический пример: производительность
import gc
import timeit
# Функция, создающая много временных объектов
def create_temps():
for _ in range(1000):
x = [1, 2, 3, 4, 5] # Временный объект
y = {"key": "value"} # Временный объект
# Вариант 1: Частые сборки всех поколений
gc.collect() # Полная сборка
time1 = timeit.timeit(create_temps, number=100)
print(f"Частые полные сборки: {time1}")
# Вариант 2: Сборка только молодых поколений
def create_temps_optimized():
for _ in range(1000):
x = [1, 2, 3, 4, 5]
y = {"key": "value"}
# Сбираем только молодые поколения
gc.collect(generation=0)
time2 = timeit.timeit(create_temps_optimized, number=100)
print(f"Оптимизированная сборка: {time2}")
print(f"Ускорение: {time1/time2:.2f}x")
Когда используется каждое поколение
# Поколение 0: частая сборка
# Типичные объекты: временные переменные, результаты функций
def function():
temp_list = [1, 2, 3] # Поколение 0
temp_str = "hello" # Поколение 0
return temp_list # temp_str удалится
# Поколение 1: средняя сборка
# Объекты, пережившие несколько сборок поколения 0
class Cache:
data = {} # Живёт дольше временных переменных
# Поколение 2: редкая сборка
# Объекты, которые живут весь процесс
global_state = {"initialized": True} # Живёт всё время
Управление сборщиком мусора
import gc
# Отключить автоматическую сборку
gc.disable()
# Создаём объекты
for _ in range(10000):
x = [1, 2, 3]
# Выполняем сборку в нужный момент (например, после критической операции)
result = critical_operation()
gc.collect() # Полная сборка
# Включить обратно
gc.enable()
# Сборка только молодых поколений (быстро)
gc.collect(generation=0)
# Сборка конкретного поколения
gc.collect(generation=1) # Сборка поколения 1
gc.collect(generation=2) # Сборка поколения 2
Реальный пример: веб-приложение
import gc
class WebApplication:
def __init__(self):
self.cache = {} # Поколение 2 — живёт долго
gc.set_threshold(700, 10, 10) # Стандартные пороги
def handle_request(self):
# Создаём временные объекты (поколение 0)
request_data = parse_request()
processed = process_data(request_data)
response = format_response(processed)
# После обработки эти объекты становятся мусором
# Поколение 0 соберётся часто — это нормально
return response
def periodic_cleanup(self):
# Запускаем полную сборку раз в час
# Когда нагрузка низкая
gc.collect() # Все поколения
Почему именно 3?
- Двух недостаточно: молодые объекты требуют другого частоты сборки, чем средние
- Четырёх избыточно: три уровня покрывают большинство реальных приложений
- Математика: (700, 10, 10) даёт хороший баланс между
- Частотой сборки (700 объектов в поколении 0 = часто)
- Размером проверяемого набора (экспоненциальное увеличение)
- Производительностью приложения