В production рост RSS у API, воркера или data pipeline не всегда означает забытый объект в списке. Частая ошибка - смотреть только на график памяти и сразу чинить GC, не выяснив, кто реально удерживает или выделяет память.
1. Начинайте с tracemalloc
tracemalloc хорош для Python-level аллокаций: кэши без eviction, глобальные dict/list, closures, task-и, references из metrics/tracing.
import tracemalloc
tracemalloc.start(25)
base = tracemalloc.take_snapshot()
def dump_memory_diff():
global base
cur = tracemalloc.take_snapshot()
for stat in cur.compare_to(base, "lineno")[:10]:
print(stat)
base = cur{}
Практический совет: в сервисе повесьте такой dump на admin endpoint, signal handler или debug job и сравнивайте snapshot-ы после прогрева, а не сразу после старта.
2. Подключайте Memray для native слоя
Если RSS растёт, а
tracemalloc почти стабилен, смотрите C/Rust extensions: numpy, pandas, cryptography, grpc, драйверы БД, compression libs.
memray run -o memray.bin python -m app
memray flamegraph memray.bin
memray table memray.bin{}
tracemalloc отвечает: какие Python allocation sites выросли. Memray помогает увидеть, где выделялась память, включая native allocations.3. Не путайте leak и аллокатор
CPython может освободить объекты, но RSS не обязан сразу упасть:
pymalloc, arenas, pools и system malloc держат память для повторного использования.Проверка гипотезы:
PYTHONMALLOC=malloc python -m app
PYTHONMALLOCSTATS=1 python -m app{}
Предупреждение: если при
PYTHONMALLOC=malloc профиль резко меняется, это может быть fragmentation / allocator behavior, а не retention leak.Порядок диагностики
* зафиксируйте RSS, heap, GC stats, размеры кэшей, очередей и pools;
* воспроизведите сценарий: прогрев - стабильный traffic - подозрительный endpoint или job;
* сравните
tracemalloc, Memray и метрики приложения;* чините конкретный owner памяти, а не абстрактный “memory leak”.
Вывод:
Надёжная диагностика утечек начинается не с GC, а с разделения Python retention, native allocations и поведения аллокатора.
