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

27.7. tracemalloc – Отслеживание выделений памятиtracemalloc – Trace memory allocations

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

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

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

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

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

27.7.1. ПримерыExamples

27.7.1.1. Отображение топ-10Display 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 загрузил 4.8 MiB данных (байт-код и константы) из модулей, а модуль collections выделил 244 KiB для построения типов namedtuple.

Смотрите Snapshot.statistics() для получения дополнительных параметров.

27.7.1.2. Вычисление различий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 загрузил 8.2 MiB данных модулей (байт-код и константы), и это на 4.4 MiB больше, чем было загружено до тестов, когда был сделан предыдущий снимок. Аналогично, модуль linecache кэшировал 940 KiB исходного кода Python для форматирования трассировок стека, всё это со времени предыдущего снимка.

Если в системе мало свободной памяти, снимки можно записать на диск с помощью метода Snapshot.dump() для последующего офлайн-анализа. Затем используйте метод Snapshot.load() для перезагрузки снимка.

27.7.1.3. Получение трассировки блока памяти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 KiB. Трассировка показывает, где модуль importlib загружал данные в последний раз: на строке import pdb модуля doctest. Трассировка может измениться, если будет загружен новый модуль.

27.7.1.4. Красивый вывод топPretty top

Код для отображения 10 строк, выделяющих больше всего памяти, с красивым выводом, игнорируя файлы <frozen importlib._bootstrap> и <unknown>:

import linecache
import os
import tracemalloc

def display_top(snapshot, group_by='lineno', limit=10):
    snapshot = snapshot.filter_traces((
        tracemalloc.Filter(False, "<frozen importlib._bootstrap>"),
        tracemalloc.Filter(False, "<unknown>"),
    ))
    top_stats = snapshot.statistics(group_by)

    print("Top %s lines" % limit)
    for index, stat in enumerate(top_stats[:limit], 1):
        frame = stat.traceback[0]
        # замените "/path/to/module/file.py" на "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()

# ... запустить приложение ...

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. Функции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.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. ФильтрFilter

class tracemalloc.Filter(inclusive: bool, filename_pattern: str, lineno: int=None, all_frames: bool=False)

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

См. функцию fnmatch.fnmatch() для синтаксиса filename_pattern. Расширения файлов '.pyc' и '.pyo' заменяются на '.py'.

Примеры:

  • Filter(True, subprocess.__file__) включает только трассы модуля subprocess
  • Filter(False, tracemalloc.__file__) исключает трассы модуля tracemalloc
  • Filter(False, "<unknown>") исключает пустые трассировки
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.3. КадрFrame

class tracemalloc.Frame

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

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

filename

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

lineno

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

27.7.2.4. СнимокSnapshot

class tracemalloc.Snapshot

Снимок трасс блоков памяти, выделенных Python.

Функция take_snapshot() создаёт экземпляр снимка.

compare_to(old_snapshot: Snapshot, group_by: str, cumulative: bool=False)

Вычисляет различия со старым снимком. Возвращает статистику в виде отсортированного списка экземпляров StatisticDiff, сгруппированных по group_by.

См. метод Snapshot.statistics() для параметров group_by и cumulative .

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

dump(filename)

Записывает снимок в файл.

Используйте load() для перезагрузки снимка.

filter_traces(filters)

Создаёт новый экземпляр Snapshot с отфильтрованной последовательностью traces; filters – это список экземпляров Filter. Если filters – пустой список, возвращает новый экземпляр Snapshot с копией трассировок.

Все включающие фильтры применяются одновременно; трасса игнорируется, если ни один включающий фильтр ей не соответствует. Трасса также игнорируется, если ей соответствует хотя бы один исключающий фильтр.

classmethod load(filename)

Загружает снимок из файла.

См. также dump().

statistics(group_by: str, cumulative: bool=False)

Возвращает статистику в виде отсортированного списка экземпляров Statistic, сгруппированных по group_by:

group_by описание
'filename' filename
'lineno' имя файла и номер строки
'traceback' трассировка

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

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

traceback_limit

Максимальное количество кадров, хранящихся в трассировке traces: результат вызова get_traceback_limit() на момент создания снимка.

traces

Трассировки всех блоков памяти, выделенных Python: последовательность экземпляров Trace.

Последовательность не упорядочена. Используйте метод Snapshot.statistics() для получения отсортированного списка статистик.

27.7.2.5. СтатистикаStatistic

class tracemalloc.Statistic

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

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

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

count

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

size

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

traceback

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

27.7.2.6. Статистика различий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, где были выделены блоки памяти, Traceback экземпляр.

27.7.2.7. ТрассаTrace

class tracemalloc.Trace

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

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

size

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

traceback

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

27.7.2.8. Traceback

class tracemalloc.Traceback

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

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

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

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

format(limit=None)

Форматирует объект traceback как список строк с переводами строк. Для получения строк исходного кода используется модуль 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())