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). Только для чтения.
-
Фильтр
-
class tracemalloc.Filter(inclusive: bool, filename_pattern: str, lineno: int = None, all_frames: bool = False, domain: int = None) -
Фильтр по следам блоков памяти.
См. функцию
fnmatch.fnmatch()для синтаксиса filename_pattern. Расширение файла'.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.
-
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: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).
-
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.
-
Трассировка
-
class tracemalloc.Trace -
Трассировка блока памяти.
Атрибут
Snapshot.traces— последовательность экземпляровTrace.Изменено в версии 3.6: Добавлен атрибут
domain.-
domain -
Адресное пространство блока памяти (
int). Только для чтения.tracemalloc использует область
0для отслеживания выделения памяти, выполненного Python. Расширения C могут использовать другие области для отслеживания других ресурсов.
-
size -
Размер блока памяти в байтах (
int).
-
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 -
Общее количество фреймов, составляющих обратную трассировку до усечения. Этот атрибут может быть установлен в
1если информация недоступна.
Изменено в версии 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())
-
© 2001–2023 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.11/library/tracemalloc.html