Spec-Zone.ru › Python 3.12

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 лучших

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

Фильтр

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.Frame

Кадр трассировки.

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

filename

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

lineno

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

Снимок

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, суммируются размер и количество блоков памяти всех кадров трассировки следа, а не только последнего. Режим cumulative может быть использован только с 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–2024 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.12/library/tracemalloc.html

Spec-Zone.ru

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