tracemalloc
— Отслеживание распределения памяти¶
Добавлено в версии 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(...))
).
интерфейс прикладного программирования¶
Функции¶
- 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()
.Добавлено в версии 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()
.
Доменный фильтр¶
- class tracemalloc.DomainFilter(inclusive: bool, domain: int)¶
Фильтруйте следы блоков памяти по их адресному пространству (домену).
Добавлено в версии 3.6.
- inclusive¶
Если inclusive равно
True
(включить), сопоставьте блоки памяти, выделенные в адресном пространствеdomain
.Если включено равно
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
, сгруппированных по key_type.Смотрите метод
Snapshot.statistics()
для получения параметров key_type и cumulative.Результат сортируется от наибольшего к наименьшему по: абсолютному значению
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
, сгруппированных по key_type:тип ключа
описание
'filename'
имя файла
'lineno'
имя файла и номер строки
'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
).
Статистический разброс¶
- 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
).
Обратная связь¶
- 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())