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: Экземпляры
DomainFilterтеперь также принимаются в filters.
-
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 -
Полное количество рамок, составляющих трассировку до усечения. Этот атрибут может быть установлен в
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())
-
© 2001–2023 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.10/library/tracemalloc.html