tracemalloc — Spåra minnesallokeringar

Tillagd i version 3.4.

Källkod: Lib/tracemalloc.py


Modulen tracemalloc är ett felsökningsverktyg för att spåra minnesblock som allokerats av Python. Den ger följande information:

  • Traceback där ett objekt allokerades

  • Statistik över allokerade minnesblock per filnamn och radnummer: total storlek, antal och genomsnittlig storlek på allokerade minnesblock

  • Beräkna skillnaderna mellan två ögonblicksbilder för att upptäcka minnesläckor

För att spåra de flesta minnesblock som allokeras av Python bör modulen startas så tidigt som möjligt genom att ställa in miljövariabeln PYTHONTRACEMALLOC till 1, eller genom att använda kommandoradsalternativet -X tracemalloc. Funktionen tracemalloc.start() kan anropas vid körning för att börja spåra Python-minnesallokeringar.

Som standard lagrar en spårning av ett allokerat minnesblock endast den senaste bilden (1 bild). För att lagra 25 bildrutor vid start: sätt miljövariabeln PYTHONTRACEMALLOC till 25, eller använd kommandoradsalternativet -X tracemalloc=25.

Exempel

Visa de 10 bästa

Visa de 10 filer som allokerar mest minne:

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)

Exempel på utdata från Python-testsviten:

[ 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

Vi kan se att Python laddade 4855 KiB data (bytekod och konstanter) från moduler och att modulen collections allokerade 244 KiB för att bygga namedtuple-typer.

Se Snapshot.statistics() för fler alternativ.

Beräkna skillnader

Ta två ögonblicksbilder och visa skillnaderna:

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)

Exempel på utdata före/efter körning av några tester i Python-testsviten:

[ 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

Vi kan se att Python har laddat 8173 KiB moduldata (bytekod och konstanter), och att detta är 4428 KiB mer än vad som hade laddats före testerna, när den föregående ögonblicksbilden togs. På samma sätt har modulen linecache cachat 940 KiB Python-källkod för att formatera spårningar, allt sedan den föregående ögonblicksbilden.

Om systemet har lite ledigt minne kan ögonblicksbilder skrivas på disk med hjälp av metoden Snapshot.dump() för att analysera ögonblicksbilden offline. Använd sedan metoden Snapshot.load() för att ladda om ögonblicksbilden.

Hämta spårningen av ett minnesblock

Kod för att visa spårningen av det största minnesblocket:

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)

Exempel på utdata från Python-testsviten (traceback begränsad till 25 ramar):

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)

Vi kan se att det mesta minnet allokerades i modulen importlib för att ladda data (bytecode och konstanter) från moduler: 870,1 KiB. Spårningen är där importlib laddade data senast: på import pdb-raden i doctest-modulen. Spårningen kan ändras om en ny modul laddas.

Snygg topp

Kod för att visa de 10 rader som allokerar mest minne med en vacker utdata, ignorerar filerna <frozen importlib._bootstrap> och <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)

Exempel på utdata från Python-testsviten:

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

Se Snapshot.statistics() för fler alternativ.

Registrera den aktuella och maximala storleken på alla spårade minnesblock

Följande kod beräknar två summor som 0 + 1 + 2 + ... ineffektivt genom att skapa en lista med dessa tal. Denna lista förbrukar mycket minne temporärt. Vi kan använda get_traced_memory() och reset_peak() för att observera den lilla minnesanvändningen efter att summan har beräknats samt den maximala minnesanvändningen under beräkningarna:

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=}")

Utdata:

first_size=664, first_peak=3592984
second_size=804, second_peak=29704

Genom att använda reset_peak() säkerställde vi att vi korrekt kunde registrera toppen under beräkningen av small_sum, även om den är mycket mindre än den totala toppstorleken för minnesblock sedan anropet till start(). Utan anropet till reset_peak() skulle second_peak fortfarande vara toppen från beräkningen av large_sum (dvs. lika med first_peak). I det här fallet är båda topparna mycket högre än den slutliga minnesanvändningen, vilket tyder på att vi kan optimera (genom att ta bort det onödiga anropet till list och skriva sum(range(...))).

API

Funktioner

tracemalloc.clear_traces()

Tydliga spår av minnesblock som allokerats av Python.

Se även stop().

tracemalloc.get_object_traceback(obj)

Hämta spårningen där Python-objektet obj allokerades. Returnerar en Traceback-instans, eller None om tracemalloc-modulen inte spårar minnesallokeringar eller inte spårade allokeringen av objektet.

Se även funktionerna gc.get_referrers() och sys.getsizeof().

tracemalloc.get_traceback_limit()

Hämta det maximala antalet bildrutor som lagras i traceback för en trace.

Modulen tracemalloc måste spåra minnesallokeringar för att få fram gränsen, annars uppstår ett undantag.

Gränsen sätts av funktionen start().

tracemalloc.get_traced_memory()

Hämta den aktuella storleken och toppstorleken på minnesblock som spåras av modulen tracemalloc som en tupel: (current: int, peak: int).

tracemalloc.reset_peak()

Ställ in den maximala storleken på minnesblock som spåras av modulen tracemalloc till den aktuella storleken.

Gör ingenting om modulen tracemalloc inte spårar minnesallokeringar.

Denna funktion ändrar bara den registrerade toppstorleken och ändrar eller rensar inte några spår, till skillnad från clear_traces(). Ögonblicksbilder som tagits med take_snapshot() före ett anrop till reset_peak() kan på ett meningsfullt sätt jämföras med ögonblicksbilder som tagits efter anropet.

Se även get_traced_memory().

Tillagd i version 3.9.

tracemalloc.get_tracemalloc_memory()

Hämtar minnesanvändningen i byte för tracemalloc-modulen som används för att lagra spår av minnesblock. Returnerar en int.

tracemalloc.is_tracing()

True om modulen tracemalloc` spårar Python-minnesallokeringar, False annars.

Se även funktionerna start() och stop().

tracemalloc.start(nframe: int = 1)

Börja spåra Python-minnesallokeringar: installera hooks på Python-minnesallokatorer. Insamlade spårningar av spårningar kommer att begränsas till nframe-ramar. Som standard lagrar en spårning av ett minnesblock endast den senaste ramen: gränsen är 1. nframe måste vara större än eller lika med 1.

Du kan fortfarande läsa det ursprungliga antalet totala bildrutor som utgjorde traceback genom att titta på attributet Traceback.total_nframe.

Att lagra mer än 1 ram är endast användbart för att beräkna statistik grupperad efter 'traceback' eller för att beräkna kumulativ statistik: se metoderna Snapshot.compare_to() och Snapshot.statistics().

Om du lagrar fler ramar ökar minnes- och processorkostnaden för modulen tracemalloc. Använd funktionen get_tracemalloc_memory() för att mäta hur mycket minne som används av modulen tracemalloc.

Miljövariabeln PYTHONTRACEMALLOC (PYTHONTRACEMALLOC=NFRAME) och kommandoradsalternativet -X tracemalloc=NFRAME kan användas för att starta spårning vid uppstart.

Se även funktionerna stop(), is_tracing() och get_traceback_limit().

tracemalloc.stop()

Sluta spåra Python-minnesallokeringar: avinstallera hooks på Python-minnesallokatorer. Rensar även alla tidigare insamlade spår av minnesblock som allokerats av Python.

Anropa funktionen take_snapshot() för att ta en ögonblicksbild av spåren innan de rensas.

Se även funktionerna start(), is_tracing() och clear_traces().

tracemalloc.take_snapshot()

Ta en ögonblicksbild av spår av minnesblock som allokerats av Python. Returnerar en ny Snapshot-instans.

Ögonblicksbilden innehåller inte minnesblock som allokerats innan modulen tracemalloc började spåra minnesallokeringar.

Spårningar av spårningar är begränsade till get_traceback_limit()-ramar. Använd parametern nframe i funktionen start() för att lagra fler ramar.

Modulen tracemalloc måste spåra minnesallokeringar för att ta en ögonblicksbild, se funktionen start().

Se även funktionen get_object_traceback().

DomainFilter

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

Filtrera spår av minnesblock efter deras adressutrymme (domän).

Tillagd i version 3.6.

inclusive

Om inclusive är True (include), matcha minnesblock som allokerats i adressrymden domain.

Om inclusive är False (exkludera), matchas minnesblock som inte allokerats i adressutrymmet domain.

domain

Adressrymd för ett minnesblock (int). Skrivskyddad egenskap.

Filtrera

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

Filtrera på spår av minnesblock.

Se funktionen fnmatch.fnmatch() för syntaxen för filename_pattern. Filändelsen '.pyc' ersätts med '.py'.

Exempel:

  • Filter(True, subprocess.__file__) innehåller bara spår av modulen subprocess

  • Filter(False, tracemalloc.__file__) utesluter spår av modulen tracemalloc

  • Filter(False, "<unknown>") utesluter tomma spårningar

Ändrad i version 3.5: Filändelsen '.pyo' ersätts inte längre med '.py'.

Ändrad i version 3.6: Lagt till attributet domain.

domain

Adressrymd för ett minnesblock (int eller None).

tracemalloc använder domänen 0 för att spåra minnesallokeringar gjorda av Python. C-tillägg kan använda andra domäner för att spåra andra resurser.

inclusive

Om inclusive är True (include), matchas endast minnesblock som allokerats i en fil med ett namn som matchar filename_pattern på radnummer lineno.

Om inclusive är False (exkludera), ignorera minnesblock som allokerats i en fil med ett namn som matchar filename_pattern på radnummer lineno.

lineno

Radnummer (int) för filtret. Om lineno är None, matchar filtret vilket radnummer som helst.

filename_pattern

Filnamnsmönster för filtret (str). Skrivskyddad egenskap.

all_frames

Om all_frames är True kontrolleras alla ramar i tracebacken. Om all_frames är False kontrolleras endast den senaste ramen.

Detta attribut har ingen effekt om traceback-gränsen är 1. Se funktionen get_traceback_limit() och attributet Snapshot.traceback_limit.

Ram

class tracemalloc.Frame

Ram för en spårning.

Klassen Traceback är en sekvens av instanser av Frame.

filename

Filnamn (str).

lineno

Radnummer (int).

Snapshot

class tracemalloc.Snapshot

Ögonblicksbild av spår av minnesblock som allokerats av Python.

Funktionen take_snapshot() skapar en snapshot-instans.

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

Beräkna skillnaderna med en gammal ögonblicksbild. Hämta statistik som en sorterad lista över StatisticDiff-instanser grupperade efter key_type.

Se metoden Snapshot.statistics() för parametrarna key_type och cumulative.

Resultatet sorteras från det största till det minsta efter: absolut värde av StatisticDiff.size_diff, StatisticDiff.size, absolut värde av StatisticDiff.count_diff, Statistic.count och sedan efter StatisticDiff.traceback.

dump(filename)

Skriv in ögonblicksbilden i en fil.

Använd load() för att ladda om ögonblicksbilden.

filter_traces(filters)

Skapa en ny Snapshot-instans med en filtrerad traces-sekvens, filter är en lista över DomainFilter- och Filter-instanser. Om filters är en tom lista, returneras en ny Snapshot-instans med en kopia av spåren.

Alla inkluderande filter tillämpas på en gång, en spårning ignoreras om inga inkluderande filter matchar den. En spårning ignoreras om minst ett exklusivt filter matchar den.

Ändrad i version 3.6: DomainFilter-instanser accepteras nu även i filters.

classmethod load(filename)

Ladda en ögonblicksbild från en fil.

Se även dump().

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

Hämta statistik som en sorterad lista över Statistic-instanser grupperade efter key_type:

key_type

beskrivning

'filename'

filename

'lineno'

filnam och radnummer

'traceback'

spårning

Om cumulative är True, kumuleras storlek och antal minnesblock för alla ramar i spårningen av en spårning, inte bara den senaste ramen. Det kumulativa läget kan endast användas med key_type lika med 'filename' och 'lineno'.

Resultatet sorteras från det största till det minsta efter: Statistic.size, Statistic.count och sedan efter Statistic.traceback.

traceback_limit

Maximalt antal bildrutor som lagras i spårningen av traces: resultatet av get_traceback_limit() när ögonblicksbilden togs.

traces

Spårningar av alla minnesblock som allokerats av Python: sekvens av Trace-instanser.

Sekvensen har en odefinierad ordning. Använd metoden Snapshot.statistics() för att få en sorterad lista med statistik.

Statistik

class tracemalloc.Statistic

Statistik över minnesallokeringar.

Snapshot.statistics() returnerar en lista med Statistic-instanser.

Se även klassen StatisticDiff.

count

Antal minnesblock (int).

size

Total storlek på minnesblock i byte (int).

traceback

Traceback där minnesblocket allokerades, Traceback instans.

StatisticDiff

class tracemalloc.StatisticDiff

Statistisk skillnad på minnesallokeringar mellan en gammal och en ny Snapshot-instans.

Snapshot.compare_to() returnerar en lista med StatisticDiff-instanser. Se även klassen Statistic.

count

Antal minnesblock i den nya ögonblicksbilden (int): 0 om minnesblocken har frigjorts i den nya ögonblicksbilden.

count_diff

Skillnad i antal minnesblock mellan den gamla och den nya ögonblicksbilden (int): 0 om minnesblocken har allokerats i den nya ögonblicksbilden.

size

Total storlek på minnesblock i byte i den nya ögonblicksbilden (int): 0 om minnesblocken har frigjorts i den nya ögonblicksbilden.

size_diff

Skillnad i total storlek på minnesblock i byte mellan den gamla och den nya ögonblicksbilden (int): 0 om minnesblocken har allokerats i den nya ögonblicksbilden.

traceback

Traceback där minnesblocken allokerades, Traceback instans.

Spåra

class tracemalloc.Trace

Spårning av ett minnesblock.

Attributet Snapshot.traces är en sekvens av instanser av Trace.

Ändrad i version 3.6: Lagt till attributet domain.

domain

Adressrymd för ett minnesblock (int). Skrivskyddad egenskap.

tracemalloc använder domänen 0 för att spåra minnesallokeringar gjorda av Python. C-tillägg kan använda andra domäner för att spåra andra resurser.

size

Storleken på minnesblocket i byte (int).

traceback

Traceback där minnesblocket allokerades, Traceback instans.

Spårning

class tracemalloc.Traceback

Sekvens av Frame-instanser sorterade från den äldsta ramen till den senaste ramen.

En traceback innehåller minst 1 ram. Om modulen tracemalloc misslyckades med att hämta en ram används filnamnet "<unknown>" på radnummer 0.

När en ögonblicksbild tas begränsas spårningar av spårningar till get_traceback_limit()-ramar. Se funktionen take_snapshot(). Det ursprungliga antalet bildrutor i traceback lagras i attributet Traceback.total_nframe. Det gör det möjligt att veta om en traceback har avkortats av traceback-gränsen.

Attributet Trace.traceback är en instans av instansen Traceback.

Ändrad i version 3.7: Ramarna sorteras nu från de äldsta till de senaste, i stället för från de senaste till de äldsta.

total_nframe

Totalt antal bildrutor som utgjorde tracebacken före trunkering. Detta attribut kan sättas till None om informationen inte är tillgänglig.

Ändrad i version 3.9: Attributet Traceback.total_nframe lades till.

format(limit=None, most_recent_first=False)

Formatera traceback som en lista med rader. Använd modulen linecache för att hämta rader från källkoden. Om limit är inställt, formatera de limit senaste ramarna om limit är positiv. I annat fall formateras de abs(limit) äldsta ramarna. Om most_recent_first är True, vänds ordningen på de formaterade ramarna och den senaste ramen returneras först istället för sist.

Liknar funktionen traceback.format_tb(), förutom att format() inte inkluderar nya rader.

Exempel:

print("Traceback (most recent call first):")
for line in traceback:
    print(line)

Utdata:

Traceback (most recent call first):
  File "test.py", line 9
    obj = Object()
  File "test.py", line 12
    tb = tracemalloc.get_object_traceback(f())