← На главную

Python 3.14.5 откатил инкрементальный GC из-за роста памяти

12.06.2026 11:59 · hackernews

В Python 3.14.0, вышедшем в октябре 2025 года, изменили сборщик мусора (GC): вместо традиционного поколенческого GC сделали инкрементальный. В 3.14.5, которая сейчас актуальна, эти изменения откатили. Пользователи жаловались на «memory pressure» — рост потребления памяти.

Как работает память в Python: объекты живут с reference count. Пока на объект кто-то ссылается, он не удаляется. Но если возникает циклическая ссылка (объект ссылается сам на себя или объекты ссылаются друг на друга), reference count не упадёт до нуля, и объект не освободится. Для таких случаев и нужен GC.

Старый поколенческий GC разбивал объекты на три поколения: новички попадают в поколение 0, выжившие переходят в старшие. Каждые 2000 аллокаций GC проверяет поколение 0, каждые 10 раз — поколение 1, и так далее. Главный минус — при полном проходе паузы могли быть очень долгими, до нескольких секунд на больших кучах.

Новый инкрементальный GC, который внедрили в 3.14.0, уменьшил количество поколений до двух (young и old) и делал меньше работы за раз. Вместо полного прохода всех поколений он собирает только young и часть old. Это резко сократило максимальное время паузы — в тесте с 50 миллионами пустых списков паузы в 3.14.4 (с инкрементальным GC) были в среднем 264 мкс, максимум 1.4 мс, а в 3.14.5 (со старым GC) — 896 мкс и почти 4 секунды. Потребление памяти — одинаковое.

Но проблема проявилась на других сценариях. Если программа постоянно создаёт «старый» мусор (объекты с циклическими ссылками, которые не удаляются сразу), инкрементальный GC очищает его медленнее. В тесте с 10 миллионами объектов, где каждую тысячу очищали список, 3.14.4 показала существенно больший RSS: 2.8 ГБ против 717 МБ в 3.14.5. Паузы при этом тоже оказались не лучше: средние 46.7 мс у 3.14.4 против 37.2 мс у 3.14.5. В худшем случае инкрементальный GC проигрывал и по памяти, и по времени.

Разработчики решили откатить изменения, потому что поддерживать два разных GC не захотели. Варианта переключения между ними сделано не было, в отличие от Java или Go. Интересно, что изменения не проходили через обычный PEP-процесс. Те, кому новый GC нравился, остались ни с чем.

Читать оригинал →