Добавить в корзинуПозвонить
Найти в Дзене
Библиотека программиста

🐍💾 Твой Python-код жрет память? Вот 11 способов это исправить

Невозможно добиться высокой производительности и масштабируемости, если приложение неэффективно использует оперативную память. Расскажем, как это исправить. Правильное управление памятью – ключ к созданию эффективного и надежного кода. В распоряжении Python-разработчиков есть встроенные механизмы, сторонние инструменты и кастомные подходы, которые помогают сократить расход памяти. Объектный пул – стратегия оптимизации памяти и производительности, которая позволяет переиспользовать объекты: Если программа постоянно создает и удаляет ресурсоемкие объекты, то этот подход помогает: Вот пример простейшего объектного пула: Результат: В Python используется сборщик мусора, который удаляет объекты, когда на них не осталось ссылок. Но иногда нам нужно создать ссылку на объект, не мешая его автоматическому удалению. Слабые ссылки позволяют ссылаться на объект без увеличения его счетчика ссылок, и тем самым помогают избежать избыточного потребления памяти – объекты будут автоматически удаляться, к
Оглавление

Невозможно добиться высокой производительности и масштабируемости, если приложение неэффективно использует оперативную память. Расскажем, как это исправить.

Правильное управление памятью – ключ к созданию эффективного и надежного кода. В распоряжении Python-разработчиков есть встроенные механизмы, сторонние инструменты и кастомные подходы, которые помогают сократить расход памяти.

Объектный пул

   Объектный пул
Объектный пул

Объектный пул – стратегия оптимизации памяти и производительности, которая позволяет переиспользовать объекты:

  • Вместо создания нового объекта программа берет его из пула.
  • Новый объект создается только в том случае, если в пуле нет доступных объектов.
  • После использования объект возвращается в пул, а не уничтожается.

Если программа постоянно создает и удаляет ресурсоемкие объекты, то этот подход помогает:

  • Снизить затраты на выделение и освобождение памяти.
  • Уменьшить нагрузку на сборщик мусора.
  • Повысить производительность кода, работающего с затратными объектами в реальном времени.

Вот пример простейшего объектного пула:

Результат:

Слабые ссылки

   Слабые ссылки
Слабые ссылки

В Python используется сборщик мусора, который удаляет объекты, когда на них не осталось ссылок. Но иногда нам нужно создать ссылку на объект, не мешая его автоматическому удалению. Слабые ссылки позволяют ссылаться на объект без увеличения его счетчика ссылок, и тем самым помогают избежать избыточного потребления памяти – объекты будут автоматически удаляться, когда они больше не нужны. Это полезно:

  • Для кэшей – чтобы объекты автоматически удалялись, когда они больше не нужны.
  • Для избежания циклических ссылок – если объект A ссылается на B, а B на A, обычный сборщик мусора может их не удалить.

Пример использования слабых ссылок:

Результат:

Статья по теме

🐍 Помнить всё. Как работает память в Python

Слоты

   Слоты
Слоты

В Python каждый объект класса по умолчанию использует словарь __dict__ для хранения своих атрибутов. Это удобно, но занимает сравнительно большой объем памяти, особенно если создается много объектов.

Если мы заранее знаем, какие атрибуты будут у объекта, мы можем использовать __slots__, чтобы экономить память. Это позволяет Python использовать массив фиксированного размера вместо словаря. В приведенном ниже примере в RegularClass все объекты хранят атрибуты в словаре, что делает их гибкими, но затратными по памяти, а SlottedClass вместо словаря использует компактный массив для снижения потребления памяти:

Результат:

Отображение файла в память

   Отображение файла в память
Отображение файла в память

Загрузка очень больших файлов в память может сделать работу программы неэффективной или даже невозможной. Решить эту проблему помогает техника отображения файлов в память – она позволяет работать с файлом, как с обычным массивом, без необходимости загружать содержимое полностью. В результате можно быстро читать и записывать данные в файл, обрабатывая только нужные фрагменты. Python предоставляет модуль mmap, который позволяет отобразить файл в память и работать с ним, как с байтовым массивом:

Модуль mmap особенно полезен:

  • При работе с огромными файлами, которые не помещаются в оперативную память.
  • Для быстрого случайного доступа к данным в файле (например, обработка больших логов или бинарных данных).
  • При многократном чтении частей файла – mmap позволяет обращаться к файлу, как к массиву, без лишних дисковых операций.

Точное определение объема памяти, используемого объектами

   Точное определение объема памяти, используемого объектами
Точное определение объема памяти, используемого объектами

Если программа использует слишком много памяти, важно выяснить, какие именно объекты в этом задействованы. Метод sys.getsizeof() позволяет узнать размер объекта, но не учитывает вложенные структуры. Например, у списка sys.getsizeof(my_list) покажет только его контейнер, но не элементы внутри. Для детального анализа потребления памяти лучше использовать более продвинутые инструменты, например, memory_profiler. Этот инструмент позволяет измерять использование памяти построчно, выявляя проблемные места в коде:

Результат:

🐍 Библиотека питониста

Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека питониста»

🐍🎓 Библиотека Python для собеса

Подтянуть свои знания по Python вы можете на нашем телеграм-канале «Библиотека Python для собеса»

🐍🧩 Библиотека задач по Python

Интересные задачи по Python для практики можно найти на нашем телеграм-канале «Библиотека задач по Python»

Эффективная обработка больших коллекций

   Эффективная обработка больших коллекций
Эффективная обработка больших коллекций

Обычный список загружает все данные сразу, и если файл очень большой, он может занять слишком много памяти:

Генератор и yield решают эту проблему – файл можно читать построчно, не загружая все содержимое целиком:

Генераторы стоит использовать при:

  • Обработке больших файлов (логов, CSV, JSON).
  • Работе с потоками данных (веб-скрапинг, работа с API).
  • При генерации последовательностей (например, бесконечные ряды чисел).

Например, так можно реализовать пословную обработку объемного текстового файла:

Кастомные структуры данных

   Кастомные структуры данных
Кастомные структуры данных

При работе с очень большими данными, обычный список list может занять слишком много памяти. В таких случаях можно хранить часть данных на диске, а не в оперативной памяти. С этой целью можно создать кастомный список, который автоматически сохраняет данные на диск, если он становится слишком большим. В приведенном ниже примере DiskBackedList – это гибрид списка и файла, который помогает работать с огромными данными без переполнения памяти:

  • Хранит данные в оперативной памяти self.memory_list.
  • Если элементов становится слишком много max_memory_items, он сбрасывает их в файл.
  • Позволяет перебирать элементы, загружая их с диска при необходимости.

Эффективная работа с большими массивами NumPy

   Эффективная работа с большими массивами NumPy
Эффективная работа с большими массивами NumPy

Аналитикам и дата-сайентистам нередко случается работать с массивами, занимающими солидную часть оперативной памяти. Решение – использовать np.memmap: он позволяет работать с массивом, как будто он находится в памяти (а на самом деле хранится на диске). С помощью этого метода можно работать с массивами, размер которых превышает объем оперативной памяти:

Кастомный кэш с автоматическим удалением устаревших данных

   Кастомный кэш с автоматическим удалением устаревших данных
Кастомный кэш с автоматическим удалением устаревших данных

В долго работающих серверных приложениях хранение временных данных в памяти значительно ускоряет работу и уменьшает нагрузку на базу данных. Чтобы не столкнуться с утечкой памяти, стоит реализовать кастомный кэш с ограниченным временем жизни данных. В приведенном ниже примере TimedCache:

  • Хранит данные в словаре, чтобы обеспечивать быстрый доступ.
  • Запоминает время добавления каждого объекта.
  • Удаляет устаревшие объекты при каждом обращении.

Результат:

Использование контекстных менеджеров для управления временными объектами

   Использование контекстных менеджеров для управления временными объектами
Использование контекстных менеджеров для управления временными объектами

Когда приложение создает много временных объектов, важно освобождать память после их использования. Контекстные менеджеры with помогают гарантированно очищать ресурсы, даже если происходит ошибка

Такой подход особенно полезен при работе с файлами, соединениями, временными объектами и другими ресурсами, которые нужно своевременно очищать, чтобы не возникали утечки памяти.

Обработка больших данных в pandas

   Обработка больших данных в pandas
Обработка больших данных в pandas

При работе с большими таблицами, состоящими из миллионов строк, неэффективно загружать весь файл в память. Pandas позволяет обрабатывать данные порциями (чанками), чтобы снизить нагрузку на память:

Этот подход идеален для работы с файлами размером в десятки гигабайт:

  • Загружается только одна часть данных за раз.
  • Можно выполнять любые операции с чанками по мере загрузки.
  • Можно использовать параллельную обработку для ускорения процесса.

В заключение

Эффективное управление памятью — это сочетание встроенных функций, внешних инструментов и оптимизаций. Если правильно подобрать технику под конкретную задачу, можно значительно ускорить работу программы и уменьшить потребление ресурсов.

***

При подготовке статьи использовалась публикация 6 Powerful Python Techniques for Efficient Memory Management.

Публикации по теме: