Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python Enhancement Proposals

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

Inhaltsverzeichnis

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 eine Traceback-Instanz zurück oder None, wenn das tracemalloc-Modul keine Speicherbelegungen verfolgt oder die Belegung des Objekts nicht verfolgt hat.

Siehe auch die Funktionen gc.get_referrers() und sys.getsizeof().

get_traceback_limit() Funktion

Ruft die maximale Anzahl von Frames ab, die im Traceback einer Spur gespeichert sind.

Das Modul tracemalloc muss 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 Modul tracemalloc verfolgt werden, als Tupel: (size: int, max_size: int).

get_tracemalloc_memory() Funktion

Ruft den Speicherverbrauch in Bytes des Moduls tracemalloc ab, das zum Speichern von Spuren von Speicherblöcken verwendet wird. Gibt eine int zurück.

is_tracing() Funktion

True, wenn das Modul tracemalloc Python-Speicherbelegungen verfolgt, andernfalls False.

Siehe auch die Funktionen start() und stop().

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 ist 1. *nframe* muss größer oder gleich 1 sein.

Das Speichern von mehr als 1 Frame 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 Methoden Snapshot.compare_to() und Snapshot.statistics().

Das Speichern von mehr Frames erhöht den Speicher- und CPU-Overhead des Moduls tracemalloc. Verwenden Sie die Funktion get_tracemalloc_memory(), um zu messen, wie viel Speicher vom Modul tracemalloc verwendet wird.

Die Umgebungsvariable PYTHONTRACEMALLOC (PYTHONTRACEMALLOC=NFRAME) und die Kommandozeilenoption -X tracemalloc=NFRAME können verwendet werden, um die Verfolgung beim Start zu aktivieren.

Siehe auch die Funktionen stop(), is_tracing() und get_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() und is_tracing().

take_snapshot() Funktion

Erstellt einen Schnappschuss von Spuren von von Python belegten Speicherblöcken. Gibt eine neue Snapshot-Instanz zurück.

Der Schnappschuss enthält keine Speicherblöcke, die belegt wurden, bevor das Modul tracemalloc mit der Verfolgung von Speicherbelegungen begann.

Tracebacks von Spuren sind auf get_traceback_limit() Frames begrenzt. Verwenden Sie den Parameter *nframe* der Funktion start(), um mehr Frames zu speichern.

Das Modul tracemalloc muss Speicherbelegungen verfolgen, um einen Schnappschuss zu erstellen, siehe Funktion start().

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 Moduls subprocess ein
  • Filter(False, tracemalloc.__file__) schließt Spuren des Moduls tracemalloc aus
  • Filter(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* None ist, stimmt der Filter mit jeder Zeilennummer überein.

filename_pattern Attribut

Dateinamensmuster des Filters (str).

all_frames Attribut

Wenn *all_frames* True ist, werden alle Frames des Tracebacks überprüft. Wenn *all_frames* False ist, wird nur der letzte Frame überprüft.

Dieses Attribut wird ignoriert, wenn das Traceback-Limit kleiner als 2 ist. Siehe die Funktion get_traceback_limit() und das Attribut Snapshot.traceback_limit.

Frame

Frame Klasse

Frame eines Tracebacks.

Die Klasse Traceback ist eine Sequenz von Frame-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 von StatisticDiff-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 von StatisticDiff.count_diff, Statistic.count und dann nach StatisticDiff.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 neue Snapshot-Instanz mit einer gefilterten traces-Sequenz. *filters* ist eine Liste von Filter-Instanzen. Wenn *filters* eine leere Liste ist, wird eine neue Snapshot-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 von Statistic-Instanzen ab, gruppiert nach *group_by*
group_by description
'filename' Dateiname
'lineno' Dateiname und Zeilennummer
'traceback' Traceback

Wenn *cumulative* True ist, 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 einem traceback_limit größer als 1 verwendet werden.

Das Ergebnis wird sortiert von der größten zur kleinsten nach: Statistic.size, Statistic.count und dann nach Statistic.traceback.

traceback_limit Attribut

Maximale Anzahl von Frames, die im Traceback von traces gespeichert sind: Ergebnis von get_traceback_limit() zum Zeitpunkt der Aufnahme des Schnappschusses.

traces Attribut

Spuren aller von Python belegten Speicherblöcke: Sequenz von Trace-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 von Statistic-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 neuen Snapshot-Instanz.

Snapshot.compare_to() gibt eine Liste von StatisticDiff-Instanzen zurück. Siehe auch die Klasse Statistic.

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.traces ist eine Sequenz von Trace-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 von Frame-Instanzen, sortiert vom letzten zum ältesten Frame.

Ein Traceback enthält mindestens 1 Frame. Wenn das Modul tracemalloc keinen Frame abrufen konnte, wird der Dateiname "<unknown>" mit der Zeilennummer 0 verwendet.

Wenn ein Schnappschuss aufgenommen wird, sind die Tracebacks von Spuren auf get_traceback_limit() Frames begrenzt. Siehe die Funktion take_snapshot().

Das Attribut Trace.traceback ist eine Instanz von Traceback.

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.


Quelle: https://github.com/python/peps/blob/main/peps/pep-0454.rst

Zuletzt geändert: 2025-02-01 08:59:27 GMT