Tracemalloc python что это
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:
The tracemalloc module which became available from Python version 3.4. The tracemalloc module allows us to monitor memory allocations in the python code. It lets us take memory snapshots at a particular point, perform various statistics on snapshots as well as perform difference between two snapshots to check object allocation between two snapshots.
As a part of this tutorial, we’ll explain how we can use tracemalloc API to trace memory usage in python code and perform various operations. We’ll be explaining the usage of various classes, methods, and attributes available through the module with the help of various examples.
If you are interested in learning about other python profilers then please feel free to check our references section which has a list of tutorials on python profilers.
Example 1¶
As a part of the first example, we’ll simply explain how to start tracing memory using tracemalloc, take snapshots, and print traced output.
Below is a list of methods which we’ll explain as a part of this example.
Below we have introduced the above-mentioned method with a simple example. We are starting tracing at the beginning and then creating three lists of integers. We have then taken a memory snapshot and printed a list of tracebacks collected by that snapshot from the starting of the traceback.
CODE
OUTPUT
Example 2¶
As a part of our second example, we’ll explain a few attributes and methods of tracemalloc and statistic objects.
Below we have explained the usage of the above-mentioned method through example.
CODE
OUTPUT
Example 3¶
As a part of this example, we have simply explained how we can start/stop tracemalloc and clear traces.
Below we have first taken a snapshot after creating three lists of integers. We have then created another list and taken snapshots again. We have then cleared all traces which are traces of 4 lists created since the beginning. We have then created the fifth list and taken snapshot again. Please make a note from the output that the 3rd snapshot has information only about the fifth list created and all traces before it is cleared. We have also explained the usage of stop() at the end.
CODE
OUTPUT
Example 4¶
As a part of our fourth example, we’ll explain how we can store a snapshot of traces into a file on disk and then load it again.
Below we have explained the usage of both methods.
CODE
OUTPUT
Example 5¶
As a part of this example, we’ll explain how we can filter out traces from a list of all traces recorded by the tracemalloc. There are two main classes provided by tracemalloc for filtering traces.
We have explained the usage of filters through the examples below. The first snapshot includes all traces for memory blocks used by python. The second snapshot includes traces of memory blocks that are not created by python. It can be due to C as numpy is built on it. The third snapshot includes entries where the filename is tracemalloc_ex5.py and the fourth snapshot excludes entries with that file names.
CODE
OUTPUT
Example 6¶
As a part of this example, we’ll explain how we can compare two snapshots and find out the difference in traces between them.
Below we have explained through example, how we can get the difference between two snapshots. Please make a note that different traces taken before the first snapshot are not present.
CODE
OUTPUT
Example 7¶
Our seventh example is exactly the same as our sixth example with the only difference that we have used a filter to remove entry related to the tracemalloc module itself.
We have specified tracemalloc filename ( tracemalloc.__file__ ) to filename_pattern of Filter class with the inclusive attribute as False to exclude entries with tracemalloc module.
CODE
OUTPUT
Example 8¶
Our eighth example is also exactly the same as the seventh example but we have specified in a different way how we can include only entries pertaining to the file which we are tracing. We have specified tracemalloc_ex8.py as a filename pattern to the Filter object. This way it’ll eliminate entries of tracemalloc module and only keep the entry of this file.
CODE
OUTPUT
This ends our small tutorial explaining how to use tracemalloc in a different way to monitor memory usage in python. Please feel free to let us know your views in the comments section.
References¶
Sunny Solanki
About: Sunny Solanki has years of experience in IT Industry. His experience involves working on projects involving Python & Java with US/Canadian banking clients. He has good hands-on with Python and its ecosystem libraries.
His main areas of interests are Machine Learning, Data Visualization and Concurrent Programming.
Apart from his tech life, he prefers reading biographies and autobiographies.
He spends too much of his time taking care of his 50+ plants.
27.7. tracemalloc — Trace memory allocations¶
The tracemalloc module is a debug tool to trace memory blocks allocated by Python. It provides the following information:
27.7.1. Examples¶
27.7.1.1. 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 4.8 MiB data (bytecode and constants) from modules and that the collections module allocated 244 KiB to build namedtuple types.
27.7.1.2. 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 8.2 MiB of module data (bytecode and constants), and that this is 4.4 MiB 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.
27.7.1.3. 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):
27.7.1.4. 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:
27.7.2. API¶
27.7.2.1. 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.
True if the tracemalloc module is tracing Python memory allocations, False otherwise.
See also start() and stop() functions.
tracemalloc. start ( nframe: int=1 ) ¶
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.
27.7.2.2. DomainFilter¶
Filter traces of memory blocks by their address space (domain).
Address space of a memory block ( int ). Read-only property.
27.7.2.3. Filter¶
Filter on traces of memory blocks.
Changed in version 3.6: Added the domain attribute.
Address space of a memory block ( int or None ).
Filename pattern of the filter ( str ). Read-only property.
27.7.2.4. Frame¶
Frame of a traceback.
The Traceback class is a sequence of Frame instances.
27.7.2.5. Snapshot¶
Snapshot of traces of memory blocks allocated by Python.
The take_snapshot() function creates a snapshot instance.
compare_to ( old_snapshot: Snapshot, group_by: str, cumulative: bool=False ) ¶
Compute the differences with an old snapshot. Get statistics as a sorted list of StatisticDiff instances grouped by group_by.
See the Snapshot.statistics() method for group_by 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 ( group_by: str, cumulative: bool=False ) ¶
Get statistics as a sorted list of Statistic instances grouped by group_by:
group_by | description |
---|---|
‘filename’ | filename |
‘lineno’ | filename and line number |
‘traceback’ | traceback |
Maximum number of frames stored in the traceback of traces : result of the get_traceback_limit() when the snapshot was taken.
Traces of all memory blocks allocated by Python: sequence of Trace instances.
The sequence has an undefined order. Use the Snapshot.statistics() method to get a sorted list of statistics.
27.7.2.6. Statistic¶
Statistic on memory allocations.
See also the StatisticDiff class.
Number of memory blocks ( int ).
Total size of memory blocks in bytes ( int ).
Traceback where the memory block was allocated, Traceback instance.
27.7.2.7. StatisticDiff¶
Statistic difference on memory allocations between an old and a new Snapshot instance.
Snapshot.compare_to() returns a list of StatisticDiff instances. See also the Statistic class.
Number of memory blocks in the new snapshot ( int ): 0 if the memory blocks have been released in the new snapshot.
Difference of number of memory blocks between the old and the new snapshots ( int ): 0 if the memory blocks have been allocated in the new snapshot.
Total size of memory blocks in bytes in the new snapshot ( int ): 0 if the memory blocks have been released in the new snapshot.
Difference of total size of memory blocks in bytes between the old and the new snapshots ( int ): 0 if the memory blocks have been allocated in the new snapshot.
Traceback where the memory blocks were allocated, Traceback instance.
27.7.2.8. Trace¶
Trace of a memory block.
The Snapshot.traces attribute is a sequence of Trace instances.
Size of the memory block in bytes ( int ).
Traceback where the memory block was allocated, Traceback instance.
27.7.2.9. Traceback¶
Sequence of Frame instances sorted from the most recent frame to the oldest frame.
A traceback contains at least 1 frame. If the tracemalloc module failed to get a frame, the filename » » at line number 0 is used.
When a snapshot is taken, tracebacks of traces are limited to get_traceback_limit() frames. See the take_snapshot() function.
The Trace.traceback attribute is an instance of Traceback instance.
Format the traceback as a list of lines with newlines. Use the linecache module to retrieve lines from the source code. If limit is set, only format the limit most recent frames.
Ультимативный гайд по поиску утечек памяти в 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 перед релизом. Я надеюсь, что этот гайд поможет вам быстрее и проще находить утечки памяти в своих приложениях