Как управлять утечками памяти в Python?

«Как управлять утечками памяти в Python?» — вопрос из категории Python, который задают на 33% собеседований Data Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В Python управление памятью в основном автоматическое (сборщик мусора), но утечки случаются. Вот мой подход к их предотвращению и диагностике:

1. Профилактика (лучшие практики):

  • Контекстные менеджеры (with): Всегда использую для работы с файлами, сетевыми соединениями (БД, сокеты) или любыми объектами, у которых есть .close() метод.
    with open('data.json', 'r') as f, db_connection.cursor() as cur:
    data = json.load(f)
    cur.execute('INSERT ...')
  • Осторожность с циклическими ссылками: Сборщик мусора (GC) их рано или поздно удалит, но для больших графов объектов это может затянуться. В таких случаях можно использовать модуль weakref для ссылок, которые не должны продлевать жизнь объекту.
  • Генераторы и итераторы: Для обработки больших объемов данных использую генераторы (yield), чтобы не загружать все в память разом.
  • Ограничение кэшей: Декоратор @functools.lru_cache(maxsize=...) или самописные кэши с TTL (time-to-live) или ограничением по размеру.

2. Диагностика и поиск утечек:

  • tracemalloc (встроенный): Идеален для поиска мест, где память растет линейно.
    import tracemalloc
    tracemalloc.start()
    # ... выполнение подозрительного кода ...
    snapshot = tracemalloc.take_snapshot()
    top_stats = snapshot.statistics('lineno')
    for stat in top_stats[:10]:
    print(stat)
  • objgraph: Позволяет визуализировать граф объектов и найти циклические ссылки.
  • memory_profiler: Показывает потребление памяти построчно для конкретной функции.
  • Мониторинг в production: Интеграция с метриками (Prometheus/Grafana) через psutil для отслеживания роста RSS-памяти процесса.

Главное правило: Если память процесса стабильно растет в долгоживущем приложении (веб-сервер, бэкенд-воркер) и не выходит на плато — это явный сигнал к исследованию.