Spec-Zone.ru › Python 3.13

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 в противном случае.

См. также функции 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)

Фильтр трассировок блоков памяти.

Синтаксис 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

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.

Описание параметров 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:

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–2025 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.13/library/tracemalloc.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API