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.
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, посмотрев на атрибут
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()
.
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)¶ Фильтр по следам блоков памяти.
Синтаксис функции 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()
для параметров ключ_тип и кумулятив.Результат сортируется от наибольшего к наименьшему по: абсолютной величине
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'
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()
. Исходное количество кадров трассировки хранится в атрибуте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())
-