Память нельзя мониторить только статикой. OOM Killer приходит неожиданно, когда забытый lru_cache или циклическая ссылка в asyncio жрут все ресурсы. Решение — фоновый поток, который раз в N секунд снимает срезы
gc.get_objects и tracemalloc, сравнивает с порогами и шлет алерт.Как работает
gc.get_objects возвращает все отслеживаемые GC объекты. Считаешь количество инстансов своих классов через Counter. tracemalloc дает раскладку по строкам кода — сразу видно, где аллоцируется больше всего. Сравниваешь снимки, ловишь аномальный рост.Пример production-сбора
import gc
import tracemalloc
from collections import Counter
_MEMORY_THRESHOLD = 50 * 1024 * 1024 # 50 MB
_OBJECT_THRESHOLD = 10000
def check_leaks():
alerts = {}
type_counts = Counter(type(o).__name__ for o in gc.get_objects())
for type_name, count in type_counts.items():
if count > _OBJECT_THRESHOLD:
alerts[f"obj_{type_name}"] = count
snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('lineno')
total_size = sum(stat.size for stat in top_stats[:10])
if total_size > _MEMORY_THRESHOLD:
alerts["memory_total"] = total_size
return alerts
Подводные камни
-
gc.get_objects замораживает GIL. При миллионе объектов пауза ощутима — решай, готов ли к этому в пиковую нагрузку.-
tracemalloc.start() дает оверхед ~20% на аллокации. Отключай в проде, когда не нужен, или включай дозировано.-
tracemalloc не видит объекты, созданные до старта. Запускай сразу после инициализации приложения.- C-расширения вроде numpy не поддаются мониторингу — учитывай при анализе.
Практический совет
Ставь пороги на 70% от лимита контейнера. Экспортируй метрики в Prometheus через
gc.get_count() и tracemalloc.get_traced_memory(). Для глубокой копки используй objgraph на визуализацию ссылок, pympler или memray. Если сработал алерт, бери py-spy или scalene — ищи конкретную причину.Вывод: Динамический мониторинг с gc.get_objects и tracemalloc — первый эшелон защиты от утечек, который позволяет поймать аномалию до падения прода.