Spec-Zone.ru › Python 3.6

tracemalloc — Отслеживание распределения памяти

Новое в версии 3.4.

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

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

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

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

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

27.7.1. Примеры

27.7.1.1. Отображение 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() для получения дополнительных вариантов.

27.7.1.2. Вычисление различий

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

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

27.7.1.3. Получение стека вызовов для блока памяти

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

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

27.7.1.4. Красивое отображение

Код для отображения 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]
        # replace "/path/to/module/file.py" with "module/file.py"
        filename = os.sep.join(frame.filename.split(os.sep)[-2:])
        print("#%s: %s:%s: %.1f KiB"
              % (index, 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() для получения дополнительных вариантов.

27.7.2. API

27.7.2.1. Функции

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

Хранение более чем 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().

27.7.2.2. DomainFilter

class tracemalloc.DomainFilter(inclusive: bool, domain: int)

Фильтр следов блоков памяти по их адресным пространствам (доменам).

Новое в версии 3.6.

inclusive

Если inclusive равно True (включить), то соответствие происходит для блоков памяти, выделенных в адресном пространстве domain.

Если inclusive равно False (исключить), то соответствие происходит для блоков памяти, не выделенных в адресном пространстве domain.

domain

Адресное пространство блока памяти (int). Только для чтения.

27.7.2.3. 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).

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.

27.7.2.4. Frame

class tracemalloc.Frame

Кадр стека вызовов.

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

filename

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

lineno

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

27.7.2.5. 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'

имя файла

'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() для получения отсортированного списка статистики.

27.7.2.6. Statistic

class tracemalloc.Statistic

Статистика выделений памяти.

Snapshot.statistics() возвращает список экземпляров Statistic.

См. также класс StatisticDiff.

count

Количество блоков памяти (int).

size

Общий размер блоков памяти в байтах (int).

traceback

Стек вызовов, где был выделен блок памяти, экземпляр Traceback.

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

27.7.2.8. Trace

class tracemalloc.Trace

След блока памяти.

Атрибут Snapshot.traces — последовательность экземпляров Trace.

size

Размер блока памяти в байтах (int).

traceback

Стек вызовов, где был выделен блок памяти, экземпляр Traceback.

27.7.2.9. Traceback

class tracemalloc.Traceback

Последовательность экземпляров Frame, отсортированных от самого последнего кадра к самому старому.

Обратная трассировка содержит как минимум 1 кадр. Если модуль tracemalloc не смог получить кадр, используется имя файла "<unknown>" на строке 0.

При создании снимка обратные трассировки следов ограничены get_traceback_limit() кадрами. См. функцию take_snapshot().

Атрибут Trace.traceback — экземпляр Traceback.

format(limit=None)

Форматирует обратную трассировку как список строк с новой строкой. Используйте модуль linecache для получения строк из исходного кода. Если задан параметр limit, форматируются только limit самых последних кадров.

Аналогично функции 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–2020 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.6/library/tracemalloc.html

Spec-Zone.ru

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