← Назад к вопросам

Зачем сборщик мусора разделяют на 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?

  1. Двух недостаточно: молодые объекты требуют другого частоты сборки, чем средние
  2. Четырёх избыточно: три уровня покрывают большинство реальных приложений
  3. Математика: (700, 10, 10) даёт хороший баланс между
    • Частотой сборки (700 объектов в поколении 0 = часто)
    • Размером проверяемого набора (экспоненциальное увеличение)
    • Производительностью приложения
Зачем сборщик мусора разделяют на 3 категории? | PrepBro