Короткий ответ
Нужно отслеживать рост потребления памяти процесса во времени под постоянной нагрузкой и находить растущие коллекции объектов с помощью tracemalloc или objgraph, обращая внимание на кэши без ограничения размера и циклические ссылки.
Как это работает подробнее
Утечка в Python обычно логическая: объекты остаются достижимыми из-за забытых ссылок — глобальных кэшей без вытеснения, подписчиков событий, которые не отписываются, замыканий, удерживающих большие объекты. tracemalloc позволяет снять два снэпшота памяти и показать, какие строки кода ответственны за наибольший прирост выделений.
Пример кода
example-271.py
import tracemalloc
tracemalloc.start()
snapshot1 = tracemalloc.take_snapshot()
cache = {}
def handle_request(req_id):
cache[req_id] = [0] * 100_000 # намеренная утечка: кэш без вытеснения, для анализа
for i in range(1000):
handle_request(i)
snapshot2 = tracemalloc.take_snapshot()
top_stats = snapshot2.compare_to(snapshot1, "lineno")
print(top_stats[0]) # покажет строку с максимальным приростом выделенийЧто использовать на практике
- Практика. В production мониторят RSS процесса — устойчивый линейный рост без выхода на плато при стабильной нагрузке указывает на утечку. Для локализации делают контролируемый нагрузочный прогон, вызывают
gc.collect()и смотрят, возвращается ли память к базовому уровню; если нет — ищут растущие структуры черезobjgraph.show_growth().
Подводные камни
- Риски. Путают нормальный рост кучи CPython (память, зарезервированная аллокатором, но пригодная для повторного использования) с реальной утечкой; игнорируют неограниченные по размеру глобальные кэши, которые растут вместе с числом уникальных ключей за время жизни процесса.