TGViewer
Zen of Python Zen of Python @zen_of_python · 18.9K subscribers
Post #4872 2.27K
Сервис убивало по нехватке памяти каждые несколько часов, но утечки в нём не было

RSS рос ступенями: 620 МБ, 890 МБ, 1,4 ГБ, 2,3 ГБ, 3,1 ГБ. Трафик, загрузка процессора и p99 при этом стояли ровно. Число объектов Python не увеличивалось, ни разбухшего словаря, ни списка, ни кэша найти не удалось. gc.collect() честно собирал циклический мусор, а RSS после него почти не опускался. Сакшам Шарма разобрал этот случай 8 июля.

Дело в разделении обязанностей. Сборщик мусора решает, какие объекты ещё достижимы, а аллокатор решает, вернутся ли освободившиеся страницы операционной системе. Освобождённый объект просто уходит обратно в кучу процесса, и пока в той же странице памяти лежит хоть один живой объект, отдать её ядру нельзя. У glibc malloc к этому добавляются кэш освобождённых блоков tcache и отдельные арены на каждый поток, так что многопоточный сервис держит несколько независимых запасов. В одном из примеров автора живые объекты занимали около 700 МБ при RSS около 2,8 ГБ.

🔘 признак, отличающий это от настоящей утечки: tracemalloc показывает стабильный объём, а RSS продолжает расти;
🔘 лечение обошлось без единой правки в коде Python, jemalloc подключается через LD_PRELOAD;
🔘 в продакшене к нему добавили PYTHONMALLOC=malloc и MALLOC_CONF=narenas:2,background_thread:true;
🔘 автор сообщает примерно о вдвое меньшем потреблении оперативной памяти после перехода;
🔘 проверить гипотезу можно и без замены аллокатора, через MALLOC_ARENA_MAX=2 и вызов malloc_trim(0).

Оговорка автора: malloc не универсально лучше встроенного pymalloc, на миллионах мелких объектов выигрыш может оказаться обратным, поэтому замену нужно мерить на своей нагрузке.

Приходилось ловить рост RSS при стабильном числе объектов, и чем в итоге объяснялось?

@zen_of_python
More from @zen_of_python
  1. Sep 20, 2026Как collections.deque хранит элементы блоками У deque два конца, поэтому легко представить…
  2. Sep 20, 2026Что ускоряет django-msgspec в Django и где он расходится с json django-msgspec заменяет ко…
  3. Sep 20, 2026Почему миллион чисел в Python занимает 35 МБ Список из миллиона целых, которые помещаются…
  4. Sep 19, 2026Как перевести Python-сервер MCP с FastMCP на SDK 2.x Свежая установка зависимостей сломала…
  5. Sep 19, 2026Как кэшировать методы чужого Python-клиента Если библиотечный клиент нельзя менять, его ме…
  6. Sep 19, 2026Где заканчивается ускорение NumPy и что выбрать дальше Векторизация выполняет цикл низкоур…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →