tracemalloc python что это

Поиск и устранение утечек памяти в Python

tracemalloc python что это. Смотреть фото tracemalloc python что это. Смотреть картинку tracemalloc python что это. Картинка про tracemalloc python что это. Фото tracemalloc python что это

Одним из основных плюсов динамически интерпретируемых языков (включая Python) является то, что с ними можно легко управлять памятью. По мере необходимости объекты (массивы и строки) динамически растут, а их память очищается, когда ей никто не пользуется. Поскольку управление памятью осуществляется самим языком, то ее утечки встречаются реже, чем в С и С++, в которых программист сам запрашивает и высвобождает память.

Например, технологический стек BuzzFeed включает в себя архитектуру микросервисов, поддерживающих более ста сервисов, большая часть которых написана на Python. Мы изучаем такие системные свойства сервисов, как память и нагрузка. В первом случае хороший сервис будет использовать и освобождать память. Он работает так, как показано на графике ниже, сообщая об использовании памяти за 3-месячный период.

tracemalloc python что это. Смотреть фото tracemalloc python что это. Смотреть картинку tracemalloc python что это. Картинка про tracemalloc python что это. Фото tracemalloc python что это

Микросервис, в котором со временем возникает утечка памяти, демонстрирует на графике пилообразные скачки. Они возникают при увеличении используемой памяти до определенного значения (например, максимальной доступной памяти), при котором сервис отключается, высвобождается вся память и происходит перезапуск.

tracemalloc python что это. Смотреть фото tracemalloc python что это. Смотреть картинку tracemalloc python что это. Картинка про tracemalloc python что это. Фото tracemalloc python что это

Иногда анализ кода помогает обнаружить участки программы, в которых основные ресурсы операционных систем (напр., дескриптор) выделяются, но не освобождаются. Такие ресурсы ограничены и в своей работе выделяют небольшое количество памяти. Эту память необходимо освободить после использования ресурса, чтобы другие могли с ним работать.

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

Инструменты

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

С помощью метода statistics() tracemalloc может отобразить на снимке упорядоченный список всех основных выделений ресурсов. В данном фрагменте кода отмечаются пять основных источников выделений памяти, которые группируются по имени файла источника.

Результат выглядит примерно так:

tracemalloc python что это. Смотреть фото tracemalloc python что это. Смотреть картинку tracemalloc python что это. Картинка про tracemalloc python что это. Фото tracemalloc python что это

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

Результат получился следующим:

tracemalloc python что это. Смотреть фото tracemalloc python что это. Смотреть картинку tracemalloc python что это. Картинка про tracemalloc python что это. Фото tracemalloc python что это

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

При обнаружении подозрительного модуля, следует найти точную строку кода, ответственную за выделение памяти. В tracemalloc можно просматривать трассировку стека для любого выделения памяти. Как и в случае с обратной трассировкой в Python, здесь нам показывается строка и модуль с выделением памяти, а также все предшествующие вызовы.

tracemalloc python что это. Смотреть фото tracemalloc python что это. Смотреть картинку tracemalloc python что это. Картинка про tracemalloc python что это. Фото tracemalloc python что это

tracemalloc python что это. Смотреть фото tracemalloc python что это. Смотреть картинку tracemalloc python что это. Картинка про tracemalloc python что это. Фото tracemalloc python что это

В первой части статьи мы увидели, что tracemalloc делает снимки памяти и предоставляет статистические данные о выделении памяти. В следующей части описываются этапы поиска утечки памяти в одном из микросервисов.

Поиск утечки данных

Несколько месяцев мы наблюдали за классическими пилообразными скачками приложения с утечкой памяти.

tracemalloc python что это. Смотреть фото tracemalloc python что это. Смотреть картинку tracemalloc python что это. Картинка про tracemalloc python что это. Фото tracemalloc python что это

tracemalloc python что это. Смотреть фото tracemalloc python что это. Смотреть картинку tracemalloc python что это. Картинка про tracemalloc python что это. Фото tracemalloc python что это

По логам одной итерации видно, что выделение памяти происходит в нескольких модулях.

tracemalloc python что это. Смотреть фото tracemalloc python что это. Смотреть картинку tracemalloc python что это. Картинка про tracemalloc python что это. Фото tracemalloc python что это

tracemalloc не является источником утечки памяти! Он попал в этот список только потому, что для своей работы ему требуется определенное количество памяти. Мы запустили сервис на пару часов, использовали DataDog для сортировки логов по модулям и обнаружили некую тенденцию роста в socket.py :

tracemalloc python что это. Смотреть фото tracemalloc python что это. Смотреть картинку tracemalloc python что это. Картинка про tracemalloc python что это. Фото tracemalloc python что это

Мы нашли возможную причину

tracemalloc python что это. Смотреть фото tracemalloc python что это. Смотреть картинку tracemalloc python что это. Картинка про tracemalloc python что это. Фото tracemalloc python что это

Изначально я предполагал, что Python и стандартная библиотека — стабильны и не имеют утечки памяти. В этой трассировке стандартной библиотекой Python 3.6 является все, кроме пакета из DataDog ddtrace/writer.py. С учетом моего предположения о целостности Python, пакет, предоставленный сторонним ресурсом, становится достойным кандидатом для дальнейшего изучения.

Все еще подтекает

tracemalloc python что это. Смотреть фото tracemalloc python что это. Смотреть картинку tracemalloc python что это. Картинка про tracemalloc python что это. Фото tracemalloc python что это

Еще один просмотр логов

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

tracemalloc python что это. Смотреть фото tracemalloc python что это. Смотреть картинку tracemalloc python что это. Картинка про tracemalloc python что это. Фото tracemalloc python что это

tracemalloc python что это. Смотреть фото tracemalloc python что это. Смотреть картинку tracemalloc python что это. Картинка про tracemalloc python что это. Фото tracemalloc python что это

Четкая наводка

Верхушка стека показывает в ssl.py вызов к peer_certificate() в строке 645. На всякий случай, хорошенько гуглим «python утечка памяти ssl peer_certificate». Там нам попадается ссылка на отчеты об ошибках в Python. К счастью, этот баг починили. Дело остается за малым — обновить образ контейнера с Python 3.6.1 до Python 3.6.4, поставить исправленную версию и проверить, прекратилась ли утечка памяти.

Все хорошо

После обновления образа мы еще раз мониторим память через DataDog. После свежего развертывания от 9 сентября выделение памяти работает без нареканий.

tracemalloc python что это. Смотреть фото tracemalloc python что это. Смотреть картинку tracemalloc python что это. Картинка про tracemalloc python что это. Фото tracemalloc python что это

Заключение

Наличие правильных инструментов в работе позволяет прочувствовать разницу между решением проблемы и бездействием. На поиск утечки памяти у нас ушло целых два месяца. tracemalloc позволяет увидеть динамику памяти в Python. Однако он не знает о распределении памяти в пакетах на С и С++. В конечном счете обнаружение утечек памяти требует терпения, настойчивости и небольшой сыскной работы.

Источник

Ультимативный гайд по поиску утечек памяти в Python

Практика показывает, что в современном мире Docker-контейнеров и оркестраторов (Kubernetes, Nomad, etc) проблема с утечкой памяти может быть обнаружена не при локальной разработке, а в ходе нагрузочного тестирования, или даже в production-среде. В этой статье рассмотрим:

Причины появления утечек в Python-приложениях.

Доступные инструменты для отладки и мониторинга работающего приложения.

Общую методику поиска утечек памяти.

У нас есть много фреймворков и технологий, которые уже «из коробки» работают замечательно, что усыпляет бдительность. В итоге иногда тревога поднимается спустя некоторое время после проблемного релиза, когда на мониторинге появляется примерно такая картина:

Утечки плохи не только тем, что приложение начинает потреблять больше памяти. С большой вероятностью также будет наблюдаться снижение работоспособности, потому что GC придется обрабатывать всё больше и больше объектов, а аллокатору Python — чаще выделять память для новых объектов.

Заранее предупрежу, что рассмотренные методы отладки приложения крайне не рекомендованы для использования в production-среде. Область их применения сводится к ситуациям:

Есть подозрение на утечку памяти. Причина абсолютно непонятна и проявляется при production-сценариях/нагрузках.

Мы разворачиваем приложение в тестовой среде и даем тестовый трафик, аналогичный тому, при котором появляется утечка.

Смотрим, какие объекты создаются, и почему память не отдается операционной системе.

Глобально утечка может произойти в следующих местах:

Код на Python. Здесь всё просто: создаются объекты в куче, которые не могут быть удалены из-за наличия ссылок.

Подключаемые библиотеки на других языках (C/C++, Rust, etc). Утечку в сторонних библиотеках искать гораздо сложнее, чем в коде на Python. Но методика есть, и мы ее рассмотрим.

Интерпретатор Python. Эти случаи редки, но возможны. Их стоит рассматривать, если остальные методы диагностики не дали результата.

Подключение к работающему приложению

PDB — старый добрый Python Debugger, о котором стали забывать из-за красивого интерфейса для отладки в современных IDE. На мой взгляд, для поиска утечек памяти крайне неудобен.

aiomonitor. Отличное решение для асинхронных приложений. Запускается в отдельной корутине и позволяет подключиться к работающему приложению с помощью NetCat. Предоставляет доступ к полноценному интерпретатору без блокировки основного приложения.

pyrasite. Запускается в отдельном процессе, и также как aiomonitor не блокирует и не останавливает основной поток, — можно смотреть текущее состояние переменных и памяти. Для работы pyrasite требуется установленный gdb. Это накладывает ограничения на использование, например, в Docker — требуется запуск контейнера с привилегированными правами и включение ptrace.

Утечки памяти: большие объекты

Это самые простые утечки памяти, потому что большие объекты очень легко отфильтровать. Для поиска будем использовать pympler и отладку через aiomonitor.

Запустим в первом окне терминала main.py:

И подключимся к нему во втором:

Нас интересует отсортированный дамп объектов GC:

Мы можем убедиться, что самым большим объектом является наша добавляемая строка:

Забавный факт: вызов pprint выводит информацию не в терминальную сессию aiomonitor, а в исходный скрипт. В то время как обычный print ведет себя наоборот.

Теперь возникает вопрос: как же понять, где этот объект был создан? Вы наверняка заметили запуск tracemalloc в самом начале файла, — он нам и поможет:

Утечки памяти: много маленьких объектов

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

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

Во время первого запуск objgraph посчитает все объекты в куче. Дальнейшие вызовы будут показывать только новые объекты. Попробуем вызвать еще раз:

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

Посмотрим на созданные объекты более пристально:

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

Мы явно видим подозрительное место, на которое следует взглянуть более пристально.

Я бы хотел обратить внимание, что за кадром осталась основная функциональность библиотеки objgraph: рисование графов связей объектов. Пожалуйста, попробуйте его, это фантастический инструмент для поиска хитрых утечек! С помощью визуализации ссылок на объект можно быстро понять, где именно осталась неудаленная ссылка.

Сторонние C-Extensions

Это наиболее тяжелый в расследовании тип утечек памяти, потому что GC работает только с PyObject. Если утекает код на C, отследить это с помощью кода на Python невозможно. Искать утечки в сторонних библиотеках следует, если:

Основная куча объектов Python не растет (с помощью objgraph и pympler не удается найти утечки памяти).

Общая память приложения на Python продолжает бесконтрольно расти.

Для тестирования создадим небольшой модуль на Cython (cython_leak.pyx):

И установочный файл (setup.py):

И сделаем скрипт для тестирования утечки (test_cython_leak.py):

Кажется, все объекты должны корректно создаваться и удаляться. На практике график работы скрипта выглядит примерно так:

Попробуем разобраться в причине с помощью Valgrind. Для этого нам понадобится suppression-файл и отключение Python-аллокатора:

После некоторого времени работы можно посмотреть отчет (нас интересуют блоки definitely lost):

В чем же причина утечки? Конечно, в отсутствии деструктора:

После повторной компиляции и запуска можно увидеть абсолютно корректную работу приложения:

Заключение

Я бы хотел отметить, что в компании мы считаем нагрузочное тестирование приложения с контролем потребления памяти одним из ключевых Quality Gate перед релизом. Я надеюсь, что этот гайд поможет вам быстрее и проще находить утечки памяти в своих приложениях

Источник

tracemalloc — Отслеживание выделения памяти¶

Добавлено в версии 3.4.

Модуль tracemalloc представляет собой средство отладки для отслеживания блоков памяти, выделенных Python. В нем содержится следующая информация:

Примеры¶

Показать топ-10¶

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

Пример вывода набора тестов Python:

Вычислить различия¶

Сделайте два снимка и просмотрите различия:

Пример вывода до/после выполнения некоторых тестов набора тестов Python:

Мы видим, что Python загрузил 8173 KiB данных модуля (байт-код и константы), и что это 4428 KiB больше, чем было загружено до тестов, когда был сделан предыдущий снимок. Аналогичным образом, модуль linecache кэширует 940 KiB Python исходного код для форматирования трейсбэки, и все это происходит со времени предыдущего снимка файловой системы.

Если в системе недостаточно свободной памяти, снимки можно записать на диск с помощью метода Snapshot.dump() для анализа снимка в автономном режиме. Затем используйте метод Snapshot.load() для перезагрузки снимка.

Получить трейсбэк блока памяти¶

Код для отображения трейсбэк самого большого блока памяти:

Пример вывода набора тестов Python (трейсбэк ограничено 25 кадрами):

Приятный вывод верха¶

Код для отображения 10 строк, выделяющих большую часть памяти с хорошим выводом, игнорируя файлы importlib._bootstrap> и :

Пример вывода набора тестов Python:

Функции¶

Очистить следы блоков памяти, выделенных Python.

tracemalloc. get_object_traceback ( obj ) ¶

Получение максимального количества фреймов, хранящихся в трейсбэк трассировки.

Чтобы получить ограничение, модуль tracemalloc должен отслеживать выделение памяти, в противном случае создается исключение.

См. также start() и stop() функции.

tracemalloc. start ( nframe: int=1 ) ¶

Прекращение трассировки Python выделений памяти: удаление хуки на Python распределителях памяти. Также очищает все ранее собранные следы блоков памяти, выделенных Python.

Вызовите take_snapshot() функцию, чтобы сделать снимок трассировок перед их очисткой.

Сделайте снимок следов блоков памяти, выделенных Python. Возвращает новый Snapshot сущность.

Моментальный снимок не включает блоки памяти, выделенные до того, как модуль tracemalloc запустил трассировку выделения памяти.

Трассировка трасс ограничена get_traceback_limit() кадрами. Использовать параметр nframe функции start() для сохранения большего количества фреймов.

DomainFilter¶

Фильтрация следов блоков памяти по их адресному пространству (домену).

Добавлено в версии 3.6.

Если inclusive False (исключить), сопоставьте блоки памяти, не выделенные в domain адресного пространства.

Адресное пространство блока памяти ( int ). Собственность только для чтения.

Фильтр¶

Фильтрация по следам блоков памяти.

Изменено в версии 3.6: Добавлен domain атрибут.

Адресное пространство блока памяти ( int или None ).

tracemalloc использует 0 домена для трассировки выделений памяти, выполняемых Python. Расширения C могут использовать другие домены для трассировки других ресурсов.

Шаблон имени файла фильтра ( str ). Собственность только для чтения.

Структура¶

Класс Traceback представляет собой последовательность Frame сущности.

Снимок¶

Снимок следов блоков памяти, выделенных Python.

Функция take_snapshot() создает сущность моментального снимка.

compare_to ( old_snapshot: Snapshot, key_type: str, cumulative: bool=False ) ¶

Вычислите различия с помощью старого снимка. Получение статистики в виде отсортированного списка StatisticDiff сущности, сгруппированных по key_type.

Параметры Snapshot.statistics() и key_type см. в методе cumulative.

Записать снимок в файл.

Использовать load() для перезагрузки снимка.

Фильтры all inclusive применяются одновременно, трассировка игнорируется, если ей не соответствуют фильтры inclusive. Трассировка игнорируется, если ей соответствует хотя бы один эксклюзивный фильтр.

Изменено в версии 3.6: DomainFilter сущности в настоящее время также принимаются в filters.

Загрузить снимок из файла.

statistics ( key_type: str, cumulative: bool=False ) ¶

Получение статистики в виде отсортированного списка Statistic сущности, сгруппированных по key_type:

key_typeописание
‘filename’filename
‘lineno’filename и line number
‘traceback’traceback

Максимальное количество фреймов, сохраненных в трейсбэк traces : результат get_traceback_limit() при создании снимка.

Следы всех блоков памяти, выделенных Python: последовательность Trace сущности.

Последовательность имеет неопределенный порядок. Использовать метод Snapshot.statistics() для получения отсортированного списка статистики.

Статистическая величина¶

Статистика по выделениям памяти.

Количество блоков памяти ( int ).

Общий размер блоков памяти в байтах ( int ).

Трассировка, где был выделен блок памяти, Traceback сущность.

StatisticDiff¶

Статистическая разница в распределении памяти между старым и новым Snapshot сущность.

Трассировка, где были выделены блоки памяти, Traceback сущность.

Трассировка¶

Трассировка блока памяти.

Snapshot.traces атрибут представляет собой последовательность Trace сущности.

Изменено в версии 3.6: Добавлен domain атрибут.

Адресное пространство блока памяти ( int ). Собственность только для чтения.

tracemalloc использует 0 домена для трассировки выделений памяти, выполняемых Python. Расширения C могут использовать другие домены для трассировки других ресурсов.

Размер блока памяти в байтах ( int ).

Трассировка, где был выделен блок памяти, Traceback сущность.

Трейсбэк¶

Последовательность Frame сущности отсортирована от самого старого фрейм до самого последнего фрейма.

В трейсбэк содержится не менее 1 фрейм. Если модулю tracemalloc не удалось получить фрейм, » » имя файла 0 в строке используемый.

Изменено в версии 3.7: Фреймы теперь сортируются от самых старых к самым последним, а не от самых последних к самым старым.

Источник

tracemalloc — Trace memory allocations¶

The tracemalloc module is a debug tool to trace memory blocks allocated by Python. It provides the following information:

Traceback where an object was allocated

Statistics on allocated memory blocks per filename and per line number: total size, number and average size of allocated memory blocks

Compute the differences between two snapshots to detect memory leaks

ExamplesВ¶

Display the top 10В¶

Display the 10 files allocating the most memory:

Example of output of the Python test suite:

We can see that Python loaded 4855 KiB data (bytecode and constants) from modules and that the collections module allocated 244 KiB to build namedtuple types.

Compute differencesВ¶

Take two snapshots and display the differences:

Example of output before/after running some tests of the Python test suite:

We can see that Python has loaded 8173 KiB of module data (bytecode and constants), and that this is 4428 KiB more than had been loaded before the tests, when the previous snapshot was taken. Similarly, the linecache module has cached 940 KiB of Python source code to format tracebacks, all of it since the previous snapshot.

If the system has little free memory, snapshots can be written on disk using the Snapshot.dump() method to analyze the snapshot offline. Then use the Snapshot.load() method reload the snapshot.

Get the traceback of a memory blockВ¶

Code to display the traceback of the biggest memory block:

Example of output of the Python test suite (traceback limited to 25 frames):

Pretty topВ¶

Code to display the 10 lines allocating the most memory with a pretty output, ignoring importlib._bootstrap> and files:

Example of output of the Python test suite:

Record the current and peak size of all traced memory blocksВ¶

FunctionsВ¶

Clear traces of memory blocks allocated by Python.

tracemalloc. get_object_traceback ( obj ) В¶

Get the traceback where the Python object obj was allocated. Return a Traceback instance, or None if the tracemalloc module is not tracing memory allocations or did not trace the allocation of the object.

Get the maximum number of frames stored in the traceback of a trace.

The tracemalloc module must be tracing memory allocations to get the limit, otherwise an exception is raised.

The limit is set by the start() function.

Set the peak size of memory blocks traced by the tracemalloc module to the current size.

Do nothing if the tracemalloc module is not tracing memory allocations.

True if the tracemalloc module is tracing Python memory allocations, False otherwise.

See also start() and stop() functions.

tracemalloc. start ( nframe : int = 1 ) В¶

You can still read the original number of total frames that composed the traceback by looking at the Traceback.total_nframe attribute.

Storing more than 1 frame is only useful to compute statistics grouped by ‘traceback’ or to compute cumulative statistics: see the Snapshot.compare_to() and Snapshot.statistics() methods.

Storing more frames increases the memory and CPU overhead of the tracemalloc module. Use the get_tracemalloc_memory() function to measure how much memory is used by the tracemalloc module.

Stop tracing Python memory allocations: uninstall hooks on Python memory allocators. Also clears all previously collected traces of memory blocks allocated by Python.

Call take_snapshot() function to take a snapshot of traces before clearing them.

Take a snapshot of traces of memory blocks allocated by Python. Return a new Snapshot instance.

The snapshot does not include memory blocks allocated before the tracemalloc module started to trace memory allocations.

Tracebacks of traces are limited to get_traceback_limit() frames. Use the nframe parameter of the start() function to store more frames.

The tracemalloc module must be tracing memory allocations to take a snapshot, see the start() function.

DomainFilterВ¶

Filter traces of memory blocks by their address space (domain).

Address space of a memory block ( int ). Read-only property.

FilterВ¶

Filter on traces of memory blocks.

Filter(True, subprocess.__file__) only includes traces of the subprocess module

Filter(False, tracemalloc.__file__) excludes traces of the tracemalloc module

Filter(False, » «) excludes empty tracebacks

Changed in version 3.6: Added the domain attribute.

Address space of a memory block ( int or None ).

tracemalloc uses the domain 0 to trace memory allocations made by Python. C extensions can use other domains to trace other resources.

Filename pattern of the filter ( str ). Read-only property.

FrameВ¶

Frame of a traceback.

The Traceback class is a sequence of Frame instances.

SnapshotВ¶

Snapshot of traces of memory blocks allocated by Python.

The take_snapshot() function creates a snapshot instance.

compare_to ( old_snapshot : Snapshot , key_type : str , cumulative : bool = False ) В¶

Compute the differences with an old snapshot. Get statistics as a sorted list of StatisticDiff instances grouped by key_type.

See the Snapshot.statistics() method for key_type and cumulative parameters.

Write the snapshot into a file.

Use load() to reload the snapshot.

Create a new Snapshot instance with a filtered traces sequence, filters is a list of DomainFilter and Filter instances. If filters is an empty list, return a new Snapshot instance with a copy of the traces.

All inclusive filters are applied at once, a trace is ignored if no inclusive filters match it. A trace is ignored if at least one exclusive filter matches it.

Changed in version 3.6: DomainFilter instances are now also accepted in filters.

Load a snapshot from a file.

statistics ( key_type : str , cumulative : bool = False ) В¶

Get statistics as a sorted list of Statistic instances grouped by key_type:

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *