PEP 454 – Hinzufügen eines neuen tracemalloc-Moduls zur Verfolgung von Python-Speicherbelegungen
- Autor:
- Victor Stinner <vstinner at python.org>
- BDFL-Delegate:
- Charles-François Natali <cf.natali at gmail.com>
- Status:
- Final
- Typ:
- Standards Track
- Erstellt:
- 03-Sep-2013
- Python-Version:
- 3.4
- Resolution:
- Python-Dev Nachricht
Zusammenfassung
Diese PEP schlägt die Hinzufügung eines neuen Moduls tracemalloc vor, um von Python belegte Speicherblöcke zu verfolgen.
Begründung
Klassische generische Werkzeuge wie Valgrind können den C-Traceback abrufen, an dem ein Speicherblock belegt wurde. Die Verwendung solcher Werkzeuge zur Analyse von Python-Speicherbelegungen hilft nicht, da die meisten Speicherblöcke in derselben C-Funktion, z. B. in PyMem_Malloc(), belegt werden. Darüber hinaus verfügt Python über einen Allokator für kleine Objekte namens „pymalloc“, der freie Blöcke zur Effizienz vorhält. Dies wird von diesen Werkzeugen nicht gut gehandhabt.
Es gibt Debugging-Werkzeuge, die für die Python-Sprache bestimmt sind, wie Heapy, Pympler und Meliae, die alle lebenden Objekte mithilfe des Garbage-Collector-Moduls auflisten (Funktionen wie gc.get_objects(), gc.get_referrers() und gc.get_referents()), ihre Größe berechnen (z. B. mit sys.getsizeof()) und Objekte nach Typ gruppieren. Diese Werkzeuge liefern eine bessere Schätzung des Speicherverbrauchs einer Anwendung. Sie sind nützlich, wenn die meisten Speicherlecks Instanzen desselben Typs sind und dieser Typ nur in wenigen Funktionen instanziiert wird. Probleme entstehen, wenn der Objekttyp sehr häufig ist, wie str oder tuple, und es schwierig ist, zu identifizieren, wo diese Objekte instanziiert werden.
Das Finden von Referenzzyklen ist ebenfalls ein schwieriges Problem. Es gibt verschiedene Werkzeuge, um ein Diagramm aller Referenzen zu zeichnen. Diese Werkzeuge können bei großen Anwendungen mit Tausenden von Objekten nicht verwendet werden, da das Diagramm zu riesig ist, um es manuell zu analysieren.
Vorschlag
Mithilfe der angepassten Allokations-API aus PEP 445 wird es einfach, einen Hook an Python-Speicherallokatoren anzubringen. Ein Hook kann interne Python-Daten inspizieren, um Python-Tracebacks abzurufen. Die Idee, den aktuellen Traceback zu erhalten, stammt aus dem Modul faulthandler. faulthandler gibt den Traceback aller Python-Threads bei einem Absturz aus. Hier ist die Idee, den Traceback des aktuellen Python-Threads zu erhalten, wenn ein Speicherblock von Python belegt wird.
Diese PEP schlägt die Hinzufügung eines neuen Moduls tracemalloc vor, ein Debugging-Werkzeug zur Verfolgung von von Python belegten Speicherblöcken. Das Modul stellt folgende Informationen bereit:
- Traceback, wo ein Objekt belegt wurde
- Statistiken über belegte Speicherblöcke pro Dateiname und Zeilennummer: Gesamtgröße, Anzahl und Durchschnittsgröße der belegten Speicherblöcke
- Berechnete Unterschiede zwischen zwei Schnappschüssen zur Erkennung von Speicherlecks
Die API des tracemalloc-Moduls ähnelt der API des faulthandler-Moduls: enable() / start(), disable() / stop() und is_enabled() / is_tracing() Funktionen, eine Umgebungsvariable (PYTHONFAULTHANDLER und PYTHONTRACEMALLOC) und eine Kommandozeilenoption -X (-X faulthandler und -X tracemalloc). Siehe die Dokumentation des faulthandler-Moduls.
Die Idee der Verfolgung von Speicherbelegungen ist nicht neu. Sie wurde erstmals im PySizer-Projekt im Jahr 2005 implementiert. PySizer wurde anders implementiert: Der Traceback wurde in Frame-Objekten gespeichert und einige Python-Typen verbanden die Spur mit dem Namen des Objekttyps. Der PySizer-Patch für CPython verursacht einen Overhead bei Leistung und Speicherbedarf, auch wenn PySizer nicht verwendet wurde. tracemalloc hängt einen Traceback an die darunterliegende Schicht, an Speicherblöcke, und hat keinen Overhead, wenn das Modul keine Speicherbelegungen verfolgt.
Das tracemalloc-Modul wurde für CPython geschrieben. Andere Implementierungen von Python können es möglicherweise nicht bereitstellen.
API
Um die meisten von Python belegten Speicherblöcke zu verfolgen, sollte das Modul so früh wie möglich gestartet werden, indem die Umgebungsvariable PYTHONTRACEMALLOC auf 1 gesetzt wird, oder indem die Kommandozeilenoption -X tracemalloc verwendet wird. Die Funktion tracemalloc.start() kann zur Laufzeit aufgerufen werden, um die Verfolgung von Python-Speicherbelegungen zu starten.
Standardmäßig speichert eine Spur eines belegten Speicherblocks nur den letzten Frame (1 Frame). Um 25 Frames beim Start zu speichern: Setzen Sie die Umgebungsvariable PYTHONTRACEMALLOC auf 25 oder verwenden Sie die Kommandozeilenoption -X tracemalloc=25. Die Funktion set_traceback_limit() kann zur Laufzeit verwendet werden, um das Limit festzulegen.
Funktionen
clear_traces() Funktion
Löscht Spuren von von Python belegten Speicherblöcken.Siehe auch
stop().
get_object_traceback(obj) Funktion
Ruft den Traceback ab, bei dem das Python-Objekt *obj* belegt wurde. Gibt eineTraceback-Instanz zurück oderNone, wenn dastracemalloc-Modul keine Speicherbelegungen verfolgt oder die Belegung des Objekts nicht verfolgt hat.Siehe auch die Funktionen
gc.get_referrers()undsys.getsizeof().
get_traceback_limit() Funktion
Ruft die maximale Anzahl von Frames ab, die im Traceback einer Spur gespeichert sind.Das Modul
tracemallocmuss Speicherbelegungen verfolgen, um das Limit abzurufen, andernfalls wird eine Ausnahme ausgelöst.Das Limit wird durch die Funktion
start()festgelegt.
get_traced_memory() Funktion
Ruft die aktuelle Größe und die maximale Größe von Speicherblöcken ab, die vom Modultracemallocverfolgt werden, als Tupel:(size: int, max_size: int).
get_tracemalloc_memory() Funktion
Ruft den Speicherverbrauch in Bytes des Modulstracemallocab, das zum Speichern von Spuren von Speicherblöcken verwendet wird. Gibt eineintzurück.
is_tracing() Funktion
True, wenn das ModultracemallocPython-Speicherbelegungen verfolgt, andernfallsFalse.Siehe auch die Funktionen
start()undstop().
start(nframe: int=1) Funktion
Startet die Verfolgung von Python-Speicherbelegungen: Installiert Hooks an Python-Speicherallokatoren. Gesammelte Tracebacks von Spuren werden auf *nframe* Frames begrenzt. Standardmäßig speichert eine Spur eines Speicherblocks nur den letzten Frame: das Limit ist1. *nframe* muss größer oder gleich1sein.Das Speichern von mehr als
1Frame ist nur nützlich für die Berechnung von Statistiken, die nach'traceback'gruppiert sind, oder für die Berechnung von kumulativen Statistiken: siehe die MethodenSnapshot.compare_to()undSnapshot.statistics().Das Speichern von mehr Frames erhöht den Speicher- und CPU-Overhead des Moduls
tracemalloc. Verwenden Sie die Funktionget_tracemalloc_memory(), um zu messen, wie viel Speicher vom Modultracemallocverwendet wird.Die Umgebungsvariable
PYTHONTRACEMALLOC(PYTHONTRACEMALLOC=NFRAME) und die Kommandozeilenoption-X tracemalloc=NFRAMEkönnen verwendet werden, um die Verfolgung beim Start zu aktivieren.Siehe auch die Funktionen
stop(),is_tracing()undget_traceback_limit().
stop() Funktion
Stoppt die Verfolgung von Python-Speicherbelegungen: Deinstalliert Hooks an Python-Speicherallokatoren. Löscht auch Spuren von von Python belegten Speicherblöcken.Rufen Sie die Funktion
take_snapshot()auf, um einen Schnappschuss der Spuren zu erstellen, bevor diese gelöscht werden.Siehe auch die Funktionen
start()undis_tracing().
take_snapshot() Funktion
Erstellt einen Schnappschuss von Spuren von von Python belegten Speicherblöcken. Gibt eine neueSnapshot-Instanz zurück.Der Schnappschuss enthält keine Speicherblöcke, die belegt wurden, bevor das Modul
tracemallocmit der Verfolgung von Speicherbelegungen begann.Tracebacks von Spuren sind auf
get_traceback_limit()Frames begrenzt. Verwenden Sie den Parameter *nframe* der Funktionstart(), um mehr Frames zu speichern.Das Modul
tracemallocmuss Speicherbelegungen verfolgen, um einen Schnappschuss zu erstellen, siehe Funktionstart().Siehe auch die Funktion
get_object_traceback().
Filter
Filter(inclusive: bool, filename_pattern: str, lineno: int=None, all_frames: bool=False) Klasse
Filter für Spuren von Speicherblöcken.Siehe die Funktion
fnmatch.fnmatch()für die Syntax von *filename_pattern*. Die Dateiendungen'.pyc'und'.pyo'werden durch'.py'ersetzt.Beispiele
Filter(True, subprocess.__file__)schließt nur Spuren des ModulssubprocesseinFilter(False, tracemalloc.__file__)schließt Spuren des ModulstracemallocausFilter(False, "<unknown>")schließt leere Tracebacks aus
inclusive Attribut
Wenn *inclusive*True(inklusive) ist, werden nur Speicherblöcke verfolgt, die in einer Datei mit einem Namen, der mit *filename_pattern* übereinstimmt, an der Zeilennummer *lineno* belegt wurden.Wenn *inclusive*
False(exklusive) ist, werden Speicherblöcke ignoriert, die in einer Datei mit einem Namen, der mit *filename_pattern* übereinstimmt, an der Zeilennummer *lineno* belegt wurden.
lineno Attribut
Zeilennummer (int) des Filters. Wenn *lineno*Noneist, stimmt der Filter mit jeder Zeilennummer überein.
filename_pattern Attribut
Dateinamensmuster des Filters (str).
all_frames Attribut
Wenn *all_frames*Trueist, werden alle Frames des Tracebacks überprüft. Wenn *all_frames*Falseist, wird nur der letzte Frame überprüft.Dieses Attribut wird ignoriert, wenn das Traceback-Limit kleiner als
2ist. Siehe die Funktionget_traceback_limit()und das AttributSnapshot.traceback_limit.
Frame
Frame Klasse
Frame eines Tracebacks.Die Klasse
Tracebackist eine Sequenz vonFrame-Instanzen.
filename Attribut
Dateiname (str).
lineno Attribut
Zeilennummer (int).
Snapshot
Snapshot Klasse
Schnappschuss von Spuren von von Python belegten Speicherblöcken.Die Funktion
take_snapshot()erstellt eine Schnappschussinstanz.
compare_to(old_snapshot: Snapshot, group_by: str, cumulative: bool=False) Methode
Berechnet die Unterschiede zu einem alten Schnappschuss. Ruft Statistiken als sortierte Liste vonStatisticDiff-Instanzen ab, gruppiert nach *group_by*.Siehe die Methode
statistics()für die Parameter *group_by* und *cumulative*.Das Ergebnis wird sortiert von der größten zur kleinsten nach: absolutem Wert von
StatisticDiff.size_diff,StatisticDiff.size, absolutem Wert vonStatisticDiff.count_diff,Statistic.countund dann nachStatisticDiff.traceback.
dump(filename) Methode
Schreibt den Schnappschuss in eine Datei.Verwenden Sie
load(), um den Schnappschuss neu zu laden.
filter_traces(filters) Methode
Erstellt eine neueSnapshot-Instanz mit einer gefiltertentraces-Sequenz. *filters* ist eine Liste vonFilter-Instanzen. Wenn *filters* eine leere Liste ist, wird eine neueSnapshot-Instanz mit einer Kopie der Spuren zurückgegeben.Alle inklusiven Filter werden gleichzeitig angewendet. Eine Spur wird ignoriert, wenn keine inklusiven Filter mit ihr übereinstimmen. Eine Spur wird ignoriert, wenn mindestens ein exklusiver Filter mit ihr übereinstimmt.
load(filename) Klassenmethode
Lädt einen Schnappschuss aus einer Datei.Siehe auch
dump().
statistics(group_by: str, cumulative: bool=False) Methode
Ruft Statistiken als sortierte Liste vonStatistic-Instanzen ab, gruppiert nach *group_by*
group_by description 'filename'Dateiname 'lineno'Dateiname und Zeilennummer 'traceback'Traceback Wenn *cumulative*
Trueist, werden Größe und Anzahl der Speicherblöcke aller Frames des Tracebacks einer Spur kumuliert, nicht nur des letzten Frames. Der kumulative Modus kann nur mit *group_by* gleich'filename'und'lineno'und einemtraceback_limitgrößer als1verwendet werden.Das Ergebnis wird sortiert von der größten zur kleinsten nach:
Statistic.size,Statistic.countund dann nachStatistic.traceback.
traceback_limit Attribut
Maximale Anzahl von Frames, die im Traceback vontracesgespeichert sind: Ergebnis vonget_traceback_limit()zum Zeitpunkt der Aufnahme des Schnappschusses.
traces Attribut
Spuren aller von Python belegten Speicherblöcke: Sequenz vonTrace-Instanzen.Die Sequenz hat eine undefinierte Reihenfolge. Verwenden Sie die Methode
Snapshot.statistics(), um eine sortierte Liste von Statistiken zu erhalten.
Statistic
Statistic Klasse
Statistik zu Speicherbelegungen.
Snapshot.statistics()gibt eine Liste vonStatistic-Instanzen zurück.Siehe auch die Klasse
StatisticDiff.
count Attribut
Anzahl der Speicherblöcke (int).
size Attribut
Gesamtgröße der Speicherblöcke in Bytes (int).
traceback Attribut
Traceback, bei dem der Speicherblock belegt wurde,Traceback-Instanz.
StatisticDiff
StatisticDiff Klasse
Statistik-Differenz bei Speicherbelegungen zwischen einer alten und einer neuenSnapshot-Instanz.
Snapshot.compare_to()gibt eine Liste vonStatisticDiff-Instanzen zurück. Siehe auch die KlasseStatistic.
count Attribut
Anzahl der Speicherblöcke im neuen Schnappschuss (int):0, wenn die Speicherblöcke im neuen Schnappschuss freigegeben wurden.
count_diff Attribut
Differenz der Anzahl der Speicherblöcke zwischen dem alten und dem neuen Schnappschuss (int):0, wenn die Speicherblöcke im neuen Schnappschuss belegt wurden.
size Attribut
Gesamtgröße der Speicherblöcke in Bytes im neuen Schnappschuss (int):0, wenn die Speicherblöcke im neuen Schnappschuss freigegeben wurden.
size_diff Attribut
Differenz der Gesamtgröße der Speicherblöcke in Bytes zwischen dem alten und dem neuen Schnappschuss (int):0, wenn die Speicherblöcke im neuen Schnappschuss belegt wurden.
traceback Attribut
Traceback, bei dem die Speicherblöcke belegt wurden,Traceback-Instanz.
Trace
Trace Klasse
Spur eines Speicherblocks.Das Attribut
Snapshot.tracesist eine Sequenz vonTrace-Instanzen.
size Attribut
Größe des Speicherblocks in Bytes (int).
traceback Attribut
Traceback, bei dem der Speicherblock belegt wurde,Traceback-Instanz.
Traceback
Traceback Klasse
Sequenz vonFrame-Instanzen, sortiert vom letzten zum ältesten Frame.Ein Traceback enthält mindestens
1Frame. Wenn das Modultracemallockeinen Frame abrufen konnte, wird der Dateiname"<unknown>"mit der Zeilennummer0verwendet.Wenn ein Schnappschuss aufgenommen wird, sind die Tracebacks von Spuren auf
get_traceback_limit()Frames begrenzt. Siehe die Funktiontake_snapshot().Das Attribut
Trace.tracebackist eine Instanz vonTraceback.
Abgelehnte Alternativen
Protokolliert Aufrufe an den Speicherallokator
Ein anderer Ansatz ist die Protokollierung von Aufrufen der Funktionen malloc(), realloc() und free(). Aufrufe können in eine Datei protokolliert oder über das Netzwerk an einen anderen Computer gesendet werden. Beispiel für einen Protokolleintrag: Name der Funktion, Größe des Speicherblocks, Adresse des Speicherblocks, Python-Traceback, wo die Belegung stattfand, Zeitstempel.
Protokolle können nicht direkt verwendet werden. Das Abrufen des aktuellen Speicherstatus erfordert das Parsen vorheriger Protokolle. Es ist beispielsweise nicht möglich, direkt den Traceback eines Python-Objekts abzurufen, wie es get_object_traceback(obj) mit Spuren tut.
Python verwendet Objekte mit einer sehr kurzen Lebensdauer und nutzt daher Speicherallokatoren extensiv. Es verfügt über einen für kleine Objekte (weniger als 512 Bytes) mit kurzer Lebensdauer optimierten Allokator. Beispielsweise ruft die Python-Testsuite durchschnittlich 270.000 Mal pro Sekunde malloc(), realloc() oder free() auf. Wenn die Größe eines Protokolleintrags 32 Bytes beträgt, erzeugt die Protokollierung 8,2 MB pro Sekunde oder 29,0 GB pro Stunde.
Die Alternative wurde abgelehnt, da sie weniger effizient ist und weniger Funktionen bietet. Das Parsen von Protokollen in einem anderen Prozess oder auf einem anderen Computer ist langsamer als die Wartung von Spuren auf belegten Speicherblöcken im selben Prozess.
Vorherige Arbeiten
- Python Memory Validator (2005-2013): kommerzieller Python-Speicher-Validator, entwickelt von Software Verification. Er verwendet die Python Reflection API.
- PySizer: Google Summer of Code 2005 Projekt von Nick Smallbone.
- Heapy (2006-2013): Teil des Guppy-PE-Projekts, geschrieben von Sverker Nilsson.
- Entwurf PEP: Support Tracking Low-Level Memory Usage in CPython (Brett Canon, 2006)
- Muppy: Projekt, entwickelt im Jahr 2008 von Robert Schuppenies.
- asizeof: ein reines Python-Modul zur Schätzung der Größe von Objekten von Jean Brouwers (2008).
- Heapmonitor: Es bietet Einrichtungen zur Größenbestimmung einzelner Objekte und kann alle Objekte bestimmter Klassen verfolgen. Es wurde 2008 von Ludwig Haehne entwickelt.
- Pympler (2008-2011): Projekt basierend auf asizeof, muppy und HeapMonitor
- objgraph (2008-2012)
- Dozer: WSGI Middleware-Version des CherryPy Memory Leak Debuggers, geschrieben von Marius Gedminas (2008-2013)
- Meliae: Python Memory Usage Analyzer, entwickelt von John A Meinel seit 2009
- gdb-heap: gdb-Skript, geschrieben in Python von Dave Malcolm (2010-2011) zur Analyse der Nutzung des Heap-Speichers
- memory_profiler: geschrieben von Fabian Pedregosa (2011-2013)
- caulk: geschrieben von Ben Timby im Jahr 2012
Siehe auch Pympler Related Work.
Links
tracemalloc
Urheberrecht
Dieses Dokument wurde gemeinfrei erklärt.
Quelle: https://github.com/python/peps/blob/main/peps/pep-0454.rst
Zuletzt geändert: 2025-02-01 08:59:27 GMT