tracemalloc — Отслеживание выделения памяти¶
Добавлено в версии 3.4.
Исходный код: Lib/tracemalloc.py
Модуль tracemalloc представляет собой средство отладки для отслеживания блоков памяти, выделенных Python. В нем содержится следующая информация:
- Трассировка, где объект был выделен
- Cтатистика по выделенным блокам памяти на имя файла и на номер строки: общий размер, количество и средний размер выделенных блоков памяти
- Вычислить различия между двумя снимками для обнаружения утечек памяти
Чтобы отследить большинство блоков памяти, выделенных Python, модуль
следует запустить как можно раньше, установив для переменной среды PYTHONTRACEMALLOC
значение 1 или используя параметр командной строки -X
tracemalloc. Функцию tracemalloc.start() можно вызвать во время выполнения, чтобы начать
трассировку Python выделения памяти.
По умолчанию трассировка выделенного блока памяти хранит только последние
фрейм (1 фрейм). Чтобы сохранить 25 фреймов при запуске, установите
для переменной среды PYTHONTRACEMALLOC значение 25 или используйте параметр
командной строки -X tracemalloc=25.
Примеры¶
Показать топ-10¶
Отобразить 10 файлов, в которых выделяется наибольшее количество памяти:
import tracemalloc
tracemalloc.start()
# ... запустить ваше приложение ...
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()
# ... запустить ваше приложение ...
snapshot1 = tracemalloc.take_snapshot()
# ... вызвать функцию утечки памяти ...
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
# Хранить 25 фреймов
tracemalloc.start(25)
# ... запустить ваше приложение ...
snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('traceback')
# выберите самый большой блок памяти
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()
# ... запустить ваше приложение ...
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().
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.get_tracemalloc_memory()¶ Получение информации об использовании памяти в байтах
tracemallocмодуля используемый для хранения следов блоков памяти. Возвращаетint.
-
tracemalloc.is_tracing()¶ True, если модульtracemallocотслеживает Python выделения памяти,Falseв противном случае.
-
tracemalloc.start(nframe: int=1)¶ Начать трассировку Python выделения памяти: установить хуки на Python распределители памяти. Собранные трейсбэки следов будут ограничены nframe кадрами. По умолчанию трассировка блока памяти хранит только самые последние фрейм: предел равен
1. nframe должно быть больше или равно1.Хранение более
1фрейм полезно только для вычисления статистики, сгруппированной по'traceback', или для вычисления кумулятивной статистики: см. методыSnapshot.compare_to()иSnapshot.statistics().Сохранение большего количества фреймов увеличивает объем памяти и процессора модуля
tracemalloc. Использовать функциюget_tracemalloc_memory()для измерения объема памяти, используемый модулемtracemalloc.Переменная окружения
PYTHONTRACEMALLOC(PYTHONTRACEMALLOC=NFRAME) и параметр командной строки-Xtracemalloc=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)¶ Фильтрация следов блоков памяти по их адресному пространству (домену).
Добавлено в версии 3.6.
-
inclusive¶ Если inclusive является
True(include), сопоставьте блоки памяти, выделенные в адресном пространствеdomain.Если inclusive
False(исключить), сопоставьте блоки памяти, не выделенные вdomainадресного пространства.
-
domain¶ Адресное пространство блока памяти (
int). Собственность только для чтения.
-
Фильтр¶
-
class
tracemalloc.Filter(inclusive: bool, filename_pattern: str, lineno: int=None, all_frames: bool=False, domain: int=None)¶ Фильтрация по следам блоков памяти.
Синтаксис
fnmatch.fnmatch()см. в функции filename_pattern. Расширение файла'.pyc'заменяется на'.py'.Examples:
Filter(True, subprocess.__file__)только включает следы модуляsubprocess,Filter(False, tracemalloc.__file__)исключает следы модуляtracemallocFilter(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) фильтра. Если linenoNone, фильтр соответствует любому номеру строки.
-
filename_pattern¶ Шаблон имени файла фильтра (
str). Собственность только для чтения.
-
all_frames¶ Если all_frames
True, проверяются все кадры трейсбэк. Если all_framesFalse, проверяется только последняя фрейм.Этот атрибут не действует, если трейсбэк предел
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сущность с копией трассировки.Фильтры 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 Если 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).
-
Трейсбэк¶
-
class
tracemalloc.Traceback¶ Последовательность
Frameсущности отсортирована от самого старого фрейм до самого последнего фрейма.В трейсбэк содержится не менее
1фрейм. Если модулюtracemallocне удалось получить фрейм,"<unknown>"имя файла0в строке используемый.При создании снимка трейсбэки трассировки ограничиваются
get_traceback_limit()кадрами. См. функциюtake_snapshot().Trace.tracebackатрибут является сущностьTraceback.Изменено в версии 3.7: Фреймы теперь сортируются от самых старых к самым последним, а не от самых последних к самым старым.
-
format(limit=None, most_recent_first=False)¶ Отформатируйте трейсбэк как список строк с новыми строками. Использовать модуль
linecacheдля извлечения строк из исходного код. Если limit установлен, отформатируйте limit самые последние кадры, если limit положительный. В противном случае отформатируйтеabs(limit)самые старые кадры. Если most_recent_firstTrue, порядок форматированных фреймов меняется на обратный, возвращая последний фрейм первым, а не последним.Аналогично функции
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())
-
