tracemalloc
— Отслеживание выделения памяти¶
Added in version 3.4.
Источник: Lib/tracemalloc.py
Модуль tracemalloc - это отладочный инструмент для отслеживания блоков памяти, выделяемых Python. Он предоставляет следующую информацию:
Отслеживание, когда объект был выделен
Статистика по выделенным блокам памяти по имени файла и номеру строки: общий размер, количество и средний размер выделенных блоков памяти
Вычислите разницу между двумя моментальными снимками, чтобы обнаружить утечки памяти
Чтобы отследить большинство блоков памяти, выделяемых Python, модуль следует запускать как можно раньше, установив переменную окружения PYTHONTRACEMALLOC
в значение 1
или используя опцию -X
tracemalloc
опции командной строки. Функция tracemalloc.start()
может быть вызвана во время выполнения, чтобы начать отслеживать выделение памяти Python.
По умолчанию при трассировке выделенного блока памяти сохраняется только последний кадр (1 кадр). Для сохранения 25 кадров при запуске: установите переменную окружения PYTHONTRACEMALLOC
в значение 25
или используйте параметр -X
tracemalloc=25
в командной строке.
Примеры¶
Отображение 10 лучших¶
Отобразите 10 файлов, выделяющих больше всего памяти:
import tracemalloc
tracemalloc.start()
# ... run your application ...
snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('lineno')
print("[ Top 10 ]")
for stat in top_stats[:10]:
print(stat)
Пример вывода тестового пакета Python:
[ Top 10 ]
<frozen importlib._bootstrap>:716: size=4855 KiB, count=39328, average=126 B
<frozen importlib._bootstrap>:284: size=521 KiB, count=3199, average=167 B
/usr/lib/python3.4/collections/__init__.py:368: size=244 KiB, count=2315, average=108 B
/usr/lib/python3.4/unittest/case.py:381: size=185 KiB, count=779, average=243 B
/usr/lib/python3.4/unittest/case.py:402: size=154 KiB, count=378, average=416 B
/usr/lib/python3.4/abc.py:133: size=88.7 KiB, count=347, average=262 B
<frozen importlib._bootstrap>:1446: size=70.4 KiB, count=911, average=79 B
<frozen importlib._bootstrap>:1454: size=52.0 KiB, count=25, average=2131 B
<string>:5: size=49.7 KiB, count=148, average=344 B
/usr/lib/python3.4/sysconfig.py:411: size=48.0 KiB, count=1, average=48.0 KiB
Мы видим, что Python загрузил данные 4855 KiB
(байткод и константы) из модулей и что модуль collections
выделил 244 KiB
для построения типов namedtuple
.
Дополнительные параметры см. в разделе Snapshot.statistics()
.
Вычислите разницу¶
Сделайте два снимка и отобразите различия:
import tracemalloc
tracemalloc.start()
# ... start your application ...
snapshot1 = tracemalloc.take_snapshot()
# ... call the function leaking memory ...
snapshot2 = tracemalloc.take_snapshot()
top_stats = snapshot2.compare_to(snapshot1, 'lineno')
print("[ Top 10 differences ]")
for stat in top_stats[:10]:
print(stat)
Пример вывода данных до/после выполнения некоторых тестов из набора тестов Python:
[ Top 10 differences ]
<frozen importlib._bootstrap>:716: size=8173 KiB (+4428 KiB), count=71332 (+39369), average=117 B
/usr/lib/python3.4/linecache.py:127: size=940 KiB (+940 KiB), count=8106 (+8106), average=119 B
/usr/lib/python3.4/unittest/case.py:571: size=298 KiB (+298 KiB), count=589 (+589), average=519 B
<frozen importlib._bootstrap>:284: size=1005 KiB (+166 KiB), count=7423 (+1526), average=139 B
/usr/lib/python3.4/mimetypes.py:217: size=112 KiB (+112 KiB), count=1334 (+1334), average=86 B
/usr/lib/python3.4/http/server.py:848: size=96.0 KiB (+96.0 KiB), count=1 (+1), average=96.0 KiB
/usr/lib/python3.4/inspect.py:1465: size=83.5 KiB (+83.5 KiB), count=109 (+109), average=784 B
/usr/lib/python3.4/unittest/mock.py:491: size=77.7 KiB (+77.7 KiB), count=143 (+143), average=557 B
/usr/lib/python3.4/urllib/parse.py:476: size=71.8 KiB (+71.8 KiB), count=969 (+969), average=76 B
/usr/lib/python3.4/contextlib.py:38: size=67.2 KiB (+67.2 KiB), count=126 (+126), average=546 B
Мы видим, что Python загрузил 8173 KiB
данных модуля (байткод и константы), и это на 4428 KiB
больше, чем было загружено до начала тестов, когда был сделан предыдущий снимок. Аналогично, модуль linecache
кэшировал 940 KiB
исходного кода Python для форматирования трассировок, и все это с момента предыдущего снимка.
Если в системе мало свободной памяти, снимки можно записать на диск, используя метод Snapshot.dump()
для анализа снимка в автономном режиме. Затем с помощью метода Snapshot.load()
перезагрузите снимок.
Получить обратную трассировку блока памяти¶
Код для отображения трассировки самого большого блока памяти:
import tracemalloc
# Store 25 frames
tracemalloc.start(25)
# ... run your application ...
snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('traceback')
# pick the biggest memory block
stat = top_stats[0]
print("%s memory blocks: %.1f KiB" % (stat.count, stat.size / 1024))
for line in stat.traceback.format():
print(line)
Пример вывода тестового пакета Python (трассировка ограничена 25 кадрами):
903 memory blocks: 870.1 KiB
File "<frozen importlib._bootstrap>", line 716
File "<frozen importlib._bootstrap>", line 1036
File "<frozen importlib._bootstrap>", line 934
File "<frozen importlib._bootstrap>", line 1068
File "<frozen importlib._bootstrap>", line 619
File "<frozen importlib._bootstrap>", line 1581
File "<frozen importlib._bootstrap>", line 1614
File "/usr/lib/python3.4/doctest.py", line 101
import pdb
File "<frozen importlib._bootstrap>", line 284
File "<frozen importlib._bootstrap>", line 938
File "<frozen importlib._bootstrap>", line 1068
File "<frozen importlib._bootstrap>", line 619
File "<frozen importlib._bootstrap>", line 1581
File "<frozen importlib._bootstrap>", line 1614
File "/usr/lib/python3.4/test/support/__init__.py", line 1728
import doctest
File "/usr/lib/python3.4/test/test_pickletools.py", line 21
support.run_doctest(pickletools)
File "/usr/lib/python3.4/test/regrtest.py", line 1276
test_runner()
File "/usr/lib/python3.4/test/regrtest.py", line 976
display_failure=not verbose)
File "/usr/lib/python3.4/test/regrtest.py", line 761
match_tests=ns.match_tests)
File "/usr/lib/python3.4/test/regrtest.py", line 1563
main()
File "/usr/lib/python3.4/test/__main__.py", line 3
regrtest.main_in_temp_cwd()
File "/usr/lib/python3.4/runpy.py", line 73
exec(code, run_globals)
File "/usr/lib/python3.4/runpy.py", line 160
"__main__", fname, loader, pkg_name)
Видно, что больше всего памяти было выделено в модуле importlib
для загрузки данных (байткода и констант) из модулей: 870.1 KiB
. Отслеживание происходит там, где importlib
загружал данные в последний раз: в строке import pdb
модуля doctest
. Обратный путь может измениться, если загружен новый модуль.
Красивый топ¶
Код для вывода 10 строк, выделяющих наибольшее количество памяти, с красивым выводом, игнорируя файлы <frozen importlib._bootstrap>
и <unknown>
:
import linecache
import os
import tracemalloc
def display_top(snapshot, key_type='lineno', limit=10):
snapshot = snapshot.filter_traces((
tracemalloc.Filter(False, "<frozen importlib._bootstrap>"),
tracemalloc.Filter(False, "<unknown>"),
))
top_stats = snapshot.statistics(key_type)
print("Top %s lines" % limit)
for index, stat in enumerate(top_stats[:limit], 1):
frame = stat.traceback[0]
print("#%s: %s:%s: %.1f KiB"
% (index, frame.filename, frame.lineno, stat.size / 1024))
line = linecache.getline(frame.filename, frame.lineno).strip()
if line:
print(' %s' % line)
other = top_stats[limit:]
if other:
size = sum(stat.size for stat in other)
print("%s other: %.1f KiB" % (len(other), size / 1024))
total = sum(stat.size for stat in top_stats)
print("Total allocated size: %.1f KiB" % (total / 1024))
tracemalloc.start()
# ... run your application ...
snapshot = tracemalloc.take_snapshot()
display_top(snapshot)
Пример вывода тестового пакета Python:
Top 10 lines
#1: Lib/base64.py:414: 419.8 KiB
_b85chars2 = [(a + b) for a in _b85chars for b in _b85chars]
#2: Lib/base64.py:306: 419.8 KiB
_a85chars2 = [(a + b) for a in _a85chars for b in _a85chars]
#3: collections/__init__.py:368: 293.6 KiB
exec(class_definition, namespace)
#4: Lib/abc.py:133: 115.2 KiB
cls = super().__new__(mcls, name, bases, namespace)
#5: unittest/case.py:574: 103.1 KiB
testMethod()
#6: Lib/linecache.py:127: 95.4 KiB
lines = fp.readlines()
#7: urllib/parse.py:476: 71.8 KiB
for a in _hexdig for b in _hexdig}
#8: <string>:5: 62.0 KiB
#9: Lib/_weakrefset.py:37: 60.0 KiB
self.data = set()
#10: Lib/base64.py:142: 59.8 KiB
_b32tab2 = [a + b for a in _b32tab for b in _b32tab]
6220 other: 3602.8 KiB
Total allocated size: 5303.1 KiB
Дополнительные параметры см. в разделе Snapshot.statistics()
.
Запишите текущий и максимальный размер всех отслеживаемых блоков памяти¶
Следующий код вычисляет две суммы типа 0 + 1 + 2 + ...
неэффективно, создавая список этих чисел. Этот список временно занимает много памяти. Мы можем использовать get_traced_memory()
и reset_peak()
, чтобы наблюдать небольшое использование памяти после вычисления суммы, а также пиковое использование памяти во время вычислений:
import tracemalloc
tracemalloc.start()
# Example code: compute a sum with a large temporary list
large_sum = sum(list(range(100000)))
first_size, first_peak = tracemalloc.get_traced_memory()
tracemalloc.reset_peak()
# Example code: compute a sum with a small temporary list
small_sum = sum(list(range(1000)))
second_size, second_peak = tracemalloc.get_traced_memory()
print(f"{first_size=}, {first_peak=}")
print(f"{second_size=}, {second_peak=}")
Выход:
first_size=664, first_peak=3592984
second_size=804, second_peak=29704
Использование reset_peak()
позволило точно зафиксировать пик во время вычисления small_sum
, даже если он намного меньше, чем общий пиковый размер блоков памяти с момента вызова start()
. Без вызова reset_peak()
пик second_peak
по-прежнему был бы пиком, полученным при вычислении large_sum
(то есть равным first_peak
). В данном случае оба пика значительно превышают конечное использование памяти, что говорит о возможности оптимизации (удаление ненужного вызова list
и запись sum(range(...))
).
API¶
Функции¶
- tracemalloc.get_object_traceback(obj)¶
Получите обратную трассировку, в которой был выделен объект Python obj. Возвращает экземпляр
Traceback
илиNone
, если модульtracemalloc
не отслеживает выделение памяти или не отследил выделение объекта.См. также функции
gc.get_referrers()
иsys.getsizeof()
.
- tracemalloc.get_traceback_limit()¶
Получение максимального количества кадров, хранящихся в трассировке трассы.
Модуль
tracemalloc
должен отслеживать выделение памяти, чтобы получить предел, иначе будет вызвано исключение.Предел задается функцией
start()
.
- tracemalloc.get_traced_memory()¶
Получение текущего и пикового размера блоков памяти, отслеживаемых модулем
tracemalloc
, в виде кортежа:(current: int, peak: int)
.
- tracemalloc.reset_peak()¶
Установите пиковый размер блоков памяти, отслеживаемых модулем
tracemalloc
, равным текущему размеру.Ничего не делает, если модуль
tracemalloc
не отслеживает выделение памяти.В отличие от
clear_traces()
эта функция изменяет только размер записываемого пика, не изменяя и не очищая никаких трасс. Снимки, сделанные с помощьюtake_snapshot()
до вызоваreset_peak()
, могут быть значимо сравнены со снимками, сделанными после вызова.См. также
get_traced_memory()
.Added in version 3.9.
- tracemalloc.get_tracemalloc_memory()¶
Получает объем памяти в байтах для модуля
tracemalloc
, используемого для хранения следов блоков памяти. Возвращает значениеint
.
- tracemalloc.is_tracing()¶
True
, если модульtracemalloc
отслеживает выделение памяти Python,False
- в противном случае.
- tracemalloc.start(nframe: int = 1)¶
Запустите трассировку выделений памяти Python: установите хуки на распределители памяти Python. Собираемые трассировки будут ограничены nframe кадрами. По умолчанию трассировка блока памяти сохраняет только самый последний кадр: ограничение составляет
1
. nframe должно быть больше или равно1
.Вы все еще можете прочитать исходное количество кадров, из которых состоит трассировка, посмотрев на атрибут
Traceback.total_nframe
.Хранение большего числа кадров, чем
1
, полезно только для вычисления статистики, сгруппированной по'traceback'
, или для вычисления кумулятивной статистики: см. методыSnapshot.compare_to()
иSnapshot.statistics()
.Хранение большего количества кадров увеличивает нагрузку на память и процессор модуля
tracemalloc
. Используйте функциюget_tracemalloc_memory()
, чтобы измерить, сколько памяти используется модулемtracemalloc
.Переменная окружения
PYTHONTRACEMALLOC
(PYTHONTRACEMALLOC=NFRAME
) и опция командной строки-X
tracemalloc=NFRAME
можно использовать для запуска трассировки при запуске.См. также функции
stop()
,is_tracing()
иget_traceback_limit()
.
- tracemalloc.stop()¶
Прекращение отслеживания выделений памяти Python: удаление хуков на распределителях памяти Python. Также очищает все ранее собранные трассировки блоков памяти, выделенных Python.
Вызовите функцию
take_snapshot()
, чтобы сделать снимок трасс перед их очисткой.См. также функции
start()
,is_tracing()
иclear_traces()
.
- tracemalloc.take_snapshot()¶
Делает снимок трассировки блоков памяти, выделенных Python. Возвращает новый экземпляр
Snapshot
.Снимок не включает блоки памяти, выделенные до того, как модуль
tracemalloc
начал отслеживать выделение памяти.Отслеживание трасс ограничено
get_traceback_limit()
кадрами. Чтобы сохранить больше кадров, используйте параметр nframe функцииstart()
.Модуль
tracemalloc
должен отслеживать выделение памяти, чтобы сделать снимок, см. функциюstart()
.См. также функцию
get_object_traceback()
.
DomainFilter¶
- class tracemalloc.DomainFilter(inclusive: bool, domain: int)¶
Фильтруйте следы блоков памяти по их адресному пространству (домену).
Added in version 3.6.
- inclusive¶
Если inclusive равно
True
(включать), то совпадают блоки памяти, выделенные в адресном пространствеdomain
.Если inclusive равно
False
(исключить), сопоставьте блоки памяти, не выделенные в адресном пространствеdomain
.
- domain¶
Адресное пространство блока памяти (
int
). Свойство только для чтения.
Фильтр¶
- class tracemalloc.Filter(inclusive: bool, filename_pattern: str, lineno: int = None, all_frames: bool = False, domain: int = None)¶
Фильтр по следам блоков памяти.
Синтаксис функции filename_pattern см. в функции
fnmatch.fnmatch()
. Расширение файла'.pyc'
заменяется на'.py'
.Примеры:
Filter(True, subprocess.__file__)
включает только следы модуляsubprocess
.Filter(False, tracemalloc.__file__)
исключает следы модуляtracemalloc
.Filter(False, "<unknown>")
исключает пустые следы
Изменено в версии 3.5: Расширение файла
'.pyo'
больше не заменяется на'.py'
.Изменено в версии 3.6: Добавлен атрибут
domain
.- domain¶
Адресное пространство блока памяти (
int
илиNone
).tracemalloc использует домен
0
для отслеживания выделений памяти, сделанных Python. Расширения C могут использовать другие домены для отслеживания других ресурсов.
- inclusive¶
Если inclusive равно
True
(включено), то сопоставьте только блоки памяти, выделенные в файле с именем, совпадающим сfilename_pattern
, с номером строкиlineno
.Если inclusive равно
False
(исключить), игнорируйте блоки памяти, выделенные в файле с именем, совпадающим сfilename_pattern
, в строке с номеромlineno
.
- lineno¶
Номер строки (
int
) фильтра. Если lineno равенNone
, фильтр соответствует любому номеру строки.
- filename_pattern¶
Шаблон имени файла фильтра (
str
). Свойство только для чтения.
- all_frames¶
Если all_frames равно
True
, проверяются все кадры обратного пути. Если all_frames равноFalse
, проверяется только самый последний кадр.Этот атрибут не влияет, если лимит возвратов к трассировке равен
1
. См. функциюget_traceback_limit()
и атрибутSnapshot.traceback_limit
.
Рама¶
Снимок¶
- class tracemalloc.Snapshot¶
Снимок трассировки блоков памяти, выделенных Python.
Функция
take_snapshot()
создает экземпляр моментального снимка.- compare_to(old_snapshot: Snapshot, key_type: str, cumulative: bool = False)¶
Вычислите разницу со старым снимком. Получение статистики в виде отсортированного списка экземпляров
StatisticDiff
, сгруппированных по ключу_типа.См. метод
Snapshot.statistics()
для параметров ключ_типа и кумулятивный.Результат сортируется от наибольшего к наименьшему по: абсолютной величине
StatisticDiff.size_diff
,StatisticDiff.size
, абсолютной величинеStatisticDiff.count_diff
,Statistic.count
и затем поStatisticDiff.traceback
.
- filter_traces(filters)¶
Создайте новый экземпляр
Snapshot
с отфильтрованной последовательностьюtraces
, filters - это список экземпляровDomainFilter
иFilter
. Если filters - пустой список, возвращается новый экземплярSnapshot
с копией трасс.Все инклюзивные фильтры применяются сразу, трасса игнорируется, если ей не соответствует ни один инклюзивный фильтр. Трасса игнорируется, если ей соответствует хотя бы один эксклюзивный фильтр.
Изменено в версии 3.6: Экземпляры
DomainFilter
теперь также принимаются в фильтрах.
- statistics(key_type: str, cumulative: bool = False)¶
Получение статистики в виде отсортированного списка экземпляров
Statistic
, сгруппированных по ключу_типа:тип ключа
описание
'filename'
имя файла
'lineno'
имя файла и номер строки
'traceback'
traceback
Если cumulative равно
True
, суммируются размер и количество блоков памяти всех кадров трассировки, а не только самого последнего. Кумулятивный режим можно использовать только при key_type, равном'filename'
и'lineno'
.Результат сортируется от наибольшего к наименьшему по:
Statistic.size
,Statistic.count
, а затем поStatistic.traceback
.
- traceback_limit¶
Максимальное количество кадров, хранящихся в трассировке
traces
: результатget_traceback_limit()
, когда был сделан снимок.
- traces¶
Трассировка всех блоков памяти, выделенных Python: последовательность экземпляров
Trace
.Последовательность имеет неопределенный порядок. Используйте метод
Snapshot.statistics()
, чтобы получить отсортированный список статистики.
Статистика¶
- class tracemalloc.Statistic¶
Статистика по выделению памяти.
Snapshot.statistics()
возвращает список экземпляровStatistic
.См. также класс
StatisticDiff
.- count¶
Количество блоков памяти (
int
).
- size¶
Общий размер блоков памяти в байтах (
int
).
StatisticDiff¶
- class tracemalloc.StatisticDiff¶
Статистическая разница в выделении памяти между старым и новым экземпляром
Snapshot
.Snapshot.compare_to()
возвращает список экземпляровStatisticDiff
. См. также классStatistic
.- count¶
Количество блоков памяти в новом снимке (
int
):0
, если блоки памяти были освобождены в новом снимке.
- count_diff¶
Разница в количестве блоков памяти между старым и новым моментальными снимками (
int
):0
, если блоки памяти были выделены в новом снимке.
- size¶
Общий размер блоков памяти в байтах в новом снимке (
int
):0
, если блоки памяти были освобождены в новом снимке.
- size_diff¶
Разница общего размера блоков памяти в байтах между старым и новым моментальными снимками (
int
):0
, если блоки памяти были выделены в новом снимке.
След¶
- class tracemalloc.Trace¶
Трассировка блока памяти.
Атрибут
Snapshot.traces
представляет собой последовательность экземпляровTrace
.Изменено в версии 3.6: Добавлен атрибут
domain
.- domain¶
Адресное пространство блока памяти (
int
). Свойство только для чтения.tracemalloc использует домен
0
для отслеживания выделений памяти, сделанных Python. Расширения C могут использовать другие домены для отслеживания других ресурсов.
- size¶
Размер блока памяти в байтах (
int
).
Traceback¶
- class tracemalloc.Traceback¶
Последовательность экземпляров
Frame
, отсортированных от самого старого до самого последнего кадра.Обратный след содержит как минимум
1
кадров. Если модульtracemalloc
не смог получить кадр, используется имя файла"<unknown>"
с номером строки0
.Когда делается снимок, отслеживание трасс ограничивается кадрами
get_traceback_limit()
. См. функциюtake_snapshot()
. Исходное количество кадров трассировки хранится в атрибутеTraceback.total_nframe
. Это позволяет узнать, не был ли откат трассы усечен в результате ограничения отката.Атрибут
Trace.traceback
является экземпляром экземпляраTraceback
.Изменено в версии 3.7: Теперь кадры сортируются от самых старых к самым последним, а не от самых последних к самым старым.
- total_nframe¶
Общее количество кадров, составляющих трассировку перед усечением. Этот атрибут может быть установлен в
None
, если информация недоступна.
Изменено в версии 3.9: Был добавлен атрибут
Traceback.total_nframe
.- format(limit=None, most_recent_first=False)¶
Оформите обратную трассировку в виде списка строк. Используйте модуль
linecache
для получения строк из исходного кода. Если задано значение limit, отформатируйте limit самых последних кадров, если limit положителен. В противном случае форматируются самые старые кадрыabs(limit)
. Если most_recent_first имеет значениеTrue
, порядок форматирования кадров меняется на противоположный, возвращая самый последний кадр первым, а не последним.Аналогична функции
traceback.format_tb()
, за исключением того, чтоformat()
не включает новые строки.Пример:
print("Traceback (most recent call first):") for line in traceback: print(line)
Выход:
Traceback (most recent call first): File "test.py", line 9 obj = Object() File "test.py", line 12 tb = tracemalloc.get_object_traceback(f())