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(...))).
API
Функции
-
tracemalloc.clear_traces() -
Очистить трассировки блоков памяти, выделенных Python.
См. также
stop().
-
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) и параметр командной строки-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(включение), соответствуют блоки памяти, выделенные в адресном пространствеdomain.Если inclusive равно
False(исключение), соответствуют блоки памяти, выделенные не в адресном пространствеdomain.
-
domain -
Адресное пространство блока памяти (
int). Атрибут только для чтения.
-
Filter
-
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.
-
Frame
Snapshot
-
class tracemalloc.Snapshot -
Снимок трассировок блоков памяти, выделенных Python.
Функция
take_snapshot()создаёт экземпляр снимка.-
compare_to(old_snapshot: Snapshot, key_type: str, cumulative: bool = False) -
Вычислить различия с предыдущим снимком. Получить статистику в виде отсортированного списка экземпляров
StatisticDiff, сгруппированных по key_type.Описание параметров key_type и cumulative см. в методе
Snapshot.statistics().Результат сортируется от наибольшего к наименьшему по следующим критериям: абсолютное значение
StatisticDiff.size_diff,StatisticDiff.size, абсолютное значениеStatisticDiff.count_diff,Statistic.count, а затемStatisticDiff.traceback.
-
dump(filename) -
Записать снимок в файл.
Чтобы загрузить снимок повторно, используйте
load().
-
filter_traces(filters) -
Создать новый экземпляр
Snapshotс отфильтрованной последовательностьюtraces; filters — это список экземпляровDomainFilterиFilter. Если filters — пустой список, возвращается новый экземплярSnapshotс копией трассировок.Все включающие фильтры применяются одновременно; трассировка игнорируется, если ей не соответствует ни один включающий фильтр. Трассировка игнорируется, если ей соответствует хотя бы один исключающий фильтр.
Изменено в версии 3.6: Теперь в filters также допускаются экземпляры
DomainFilter.
-
classmethod load(filename) -
Загрузить снимок из файла.
См. также
dump().
-
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().
-
Statistic
-
class tracemalloc.Statistic -
Статистика выделения памяти.
Snapshot.statistics()возвращает список экземпляровStatistic.См. также класс
StatisticDiff.-
count -
Количество блоков памяти (
int).
-
size -
Общий размер блоков памяти в байтах (
int).
-
traceback -
Трассировка стека, в которой был выделен блок памяти; экземпляр
Traceback.
-
StatisticDiff
-
class tracemalloc.StatisticDiff -
Разница статистики выделения памяти между старым и новым экземплярами
Snapshot.Snapshot.compare_to()возвращает список экземпляровStatisticDiff. См. также классStatistic.-
count -
Количество блоков памяти в новом снимке (
int):0, если в новом снимке блоки памяти были освобождены.
-
count_diff -
Разница в количестве блоков памяти между старым и новым снимками (
int):0, если в новом снимке были выделены блоки памяти.
-
size -
Общий размер блоков памяти в байтах в новом снимке (
int):0, если в новом снимке блоки памяти были освобождены.
-
size_diff -
Разница в общем размере блоков памяти в байтах между старым и новым снимками (
int):0, если в новом снимке были выделены блоки памяти.
-
traceback -
Трассировка стека, в которой были выделены блоки памяти; экземпляр
Traceback.
-
Trace
-
class tracemalloc.Trace -
Трассировка блока памяти.
Атрибут
Snapshot.tracesпредставляет собой последовательность экземпляровTrace.Изменено в версии 3.6: Добавлен атрибут
domain.-
domain -
Адресное пространство блока памяти (
int). Атрибут только для чтения.Модуль tracemalloc использует домен
0для отслеживания выделений памяти, выполняемых Python. Расширения C могут использовать другие домены для отслеживания других ресурсов.
-
size -
Размер блока памяти в байтах (
int).
-
traceback -
Трассировка стека, в которой был выделен блок памяти; экземпляр
Traceback.
-
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 кадров. В противном случае форматируются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())
-
© 2001–2024 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.12/library/tracemalloc.html