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.

Приклади

Покажіть 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 КіБ вихідного коду 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() для отримання додаткових параметрів.

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

DomainFilter

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

Фільтрувати сліди блоків пам’яті за їх адресним простором (доменом).

Нове в версії 3.6.

inclusive

Якщо inclusive має значення True (include), збігаються блоки пам’яті, виділені в адресному просторі 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(), щоб дізнатися про синтаксис шаблон_назви_файлу. Розширення файлу '.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 (include), збігаються лише блоки пам’яті, виділені у файлі з іменем, яке відповідає 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

опис

'ім'я файлу'

ім’я файлу

''лінено'

ім’я файлу та номер рядка

'відстеження''

простежити

Якщо cumulative має значення True, кумулювати розмір і кількість блоків пам’яті всіх кадрів зворотного відстеження трасування, а не лише останнього кадру. Кумулятивний режим можна використовувати лише з key_type, що дорівнює 'filename' і 'lineno'.

Результат сортується від найбільшого до найменшого за: Statistic.size, Statistic.count, а потім за Statistic.traceback.

traceback_limit

Максимальна кількість кадрів, що зберігаються у зворотній трасуванні traces: результату get_traceback_limit() під час створення знімка.

traces

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

Послідовність має невизначений порядок. Використовуйте метод Snapshot.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.

Слід

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.

When a snapshot is taken, tracebacks of traces are limited to get_traceback_limit() frames. See the take_snapshot() function.

Атрибут Trace.traceback є екземпляром екземпляра Traceback.

Змінено в версії 3.7: Кадри тепер сортуються від найстаріших до найновіших, а не від останніх до найстаріших.

format(limit=None, most_recent_first=False)

Format the traceback as a list of lines with newlines. Use the linecache module to retrieve lines from the source code. If limit is set, format the limit most recent frames if limit is positive. Otherwise, format the abs(limit) oldest frames. If most_recent_first is True, the order of the formatted frames is reversed, returning the most recent frame first instead of last.

Подібно до функції 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())