Документация Python неофициальный перевод
Содержание страницы

tracemalloc – Трассировка выделений памятиtracemalloc – Trace memory allocations

Добавлено в версии 3.4.

Исходный код: Lib/tracemalloc.py


Модуль tracemalloc – инструмент отладки для трассировки блоков памяти, выделяемых Python. Он предоставляет следующую информацию:

  • Трассировка места выделения объекта

  • Статистика по выделенным блокам памяти для каждого имени файла и номера строки: общий размер, количество и средний размер выделенных блоков памяти

  • Вычисление различий между двумя снимками для обнаружения утечек памяти

Чтобы трассировать большинство блоков памяти, выделяемых Python, модуль следует запускать как можно раньше, установив переменную окружения PYTHONTRACEMALLOC в 1 или используя опцию командной строки -X tracemalloc. Функцию tracemalloc.start() можно вызвать во время выполнения, чтобы начать трассировку выделений памяти Python.

По умолчанию трасса выделенного блока памяти сохраняет только самый последний кадр (1 кадр). Чтобы сохранить 25 кадров при запуске: установите переменную окружения PYTHONTRACEMALLOC в 25 или используйте опцию командной строки -X tracemalloc=25.

ПримерыExamples

Показать 10 лучшихDisplay the top 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().

Вычисление различийCompute differences

Сделать два снимка и отобразить различия:

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() для перезагрузки снимка.

Получить трассировку блока памятиGet the traceback of a memory block

Код для отображения трассировки самого большого блока памяти:

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. Трассировка может измениться, если будет загружен новый модуль.

Pretty top

Код для отображения 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().

Записывает текущий и пиковый размер всех отслеживаемых блоков памятиRecord the current and peak size of all traced memory blocks

Следующий код неэффективно вычисляет две суммы наподобие 0 + 1 + 2 + ..., создавая список этих чисел. Этот список временно потребляет много памяти. С помощью get_traced_memory() и reset_peak() можно наблюдать небольшой расход памяти после вычисления суммы, а также пиковый расход памяти во время вычислений:

import tracemalloc

tracemalloc.start()

# Пример кода: вычисление суммы с большим временным списком
large_sum = sum(list(range(100000)))

first_size, first_peak = tracemalloc.get_traced_memory()

tracemalloc.reset_peak()

# Пример кода: вычисление суммы с малым временным списком
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

ФункцииFunctions

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.

См. также функции start() и stop().

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) и опция командной строки -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). Свойство только для чтения.

Filter

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.

ФреймFrame

class tracemalloc.Frame

Фрейм трассировки.

Класс Traceback представляет собой последовательность экземпляров Frame.

filename

Имя файла (str).

lineno

Номер строки (int).

СнимокSnapshot

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'

filename

'lineno'

имя файла и номер строки

'traceback'

traceback

Если cumulative установлен в True, размер и количество блоков памяти суммируются для всех фреймов traceback трассы, а не только для самого последнего. Накопительный режим можно использовать только с key_type, равным 'filename' и 'lineno'.

Результат сортируется от наибольшего к наименьшему по: Statistic.size, Statistic.count, а затем по Statistic.traceback.

traceback_limit

Максимальное количество фреймов, хранящихся в traceback 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 самых новых кадров, если 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())