Speicherverwaltung

Übersicht

Die Speicherverwaltung in Python umfasst einen privaten Heap, der alle Python-Objekte und Datenstrukturen enthält. Die Verwaltung dieses privaten Heaps wird intern vom Python-Speichermanager sichergestellt. Der Python-Speichermanager besteht aus verschiedenen Komponenten, die sich mit verschiedenen Aspekten der dynamischen Speicherverwaltung befassen, wie z. B. Teilen, Segmentieren, Vorabzuweisung oder Caching.

Auf der untersten Ebene stellt ein Rohspeicherallokator sicher, dass im privaten Heap genügend Speicherplatz für alle Python-bezogenen Daten vorhanden ist, indem er mit dem Speichermanager des Betriebssystems interagiert. Oberhalb des Rohspeicherallokators arbeiten mehrere objektspezifische Allokatoren auf demselben Heap und implementieren unterschiedliche Speicherverwaltungsrichtlinien, die an die Besonderheiten jedes Objekttyps angepasst sind. Beispielsweise werden Integer-Objekte anders im Heap verwaltet als Strings, Tupel oder Dictionaries, da Integer unterschiedliche Speicheranforderungen und Kompromisse zwischen Geschwindigkeit und Speicherplatz implizieren. Der Python-Speichermanager delegiert daher einige Arbeiten an die objektspezifischen Allokatoren, stellt jedoch sicher, dass letztere innerhalb der Grenzen des privaten Heaps arbeiten.

Es ist wichtig zu verstehen, dass die Verwaltung des Python-Heaps durch den Interpreter selbst erfolgt und der Benutzer keine Kontrolle darüber hat, auch wenn er regelmäßig Objektzeiger auf Speicherblöcke innerhalb dieses Heaps manipuliert. Die Zuweisung von Heap-Speicher für Python-Objekte und andere interne Puffer erfolgt nach Bedarf durch den Python-Speichermanager über die in diesem Dokument aufgeführten Python/C-API-Funktionen.

Um Speicherbeschädigung zu vermeiden, sollten Erweiterungsschreiber niemals versuchen, Python-Objekte mit den von der C-Bibliothek exportierten Funktionen zu bearbeiten: malloc(), calloc(), realloc() und free(). Dies führt zu gemischten Aufrufen zwischen dem C-Allokator und dem Python-Speichermanager mit fatalen Folgen, da sie unterschiedliche Algorithmen implementieren und auf unterschiedlichen Heaps arbeiten. Man kann jedoch sicher Speicherblöcke mit dem C-Bibliotheksallokator für individuelle Zwecke zuweisen und freigeben, wie im folgenden Beispiel gezeigt wird

PyObject *res;
char *buf = (char *) malloc(BUFSIZ); /* for I/O */

if (buf == NULL)
    return PyErr_NoMemory();
...Do some I/O operation involving buf...
res = PyBytes_FromString(buf);
free(buf); /* malloc'ed */
return res;

In diesem Beispiel wird die Speicheranforderung für den E/A-Puffer vom C-Bibliotheksallokator bearbeitet. Der Python-Speichermanager ist nur an der Zuweisung des Byte-Objekts beteiligt, das als Ergebnis zurückgegeben wird.

In den meisten Fällen wird jedoch empfohlen, Speicher aus dem Python-Heap zuzuweisen, insbesondere weil dieser vom Python-Speichermanager kontrolliert wird. Dies ist beispielsweise erforderlich, wenn der Interpreter mit neuen Objekttypen in C erweitert wird. Ein weiterer Grund für die Verwendung des Python-Heaps ist der Wunsch, den Python-Speichermanager über den Speicherbedarf des Erweiterungsmoduls zu *informieren*. Selbst wenn der angeforderte Speicher ausschließlich für interne, hochspezifische Zwecke verwendet wird, führt die Delegation aller Speicheranforderungen an den Python-Speichermanager dazu, dass der Interpreter ein genaueres Bild seines gesamten Speicher-Footprints erhält. Folglich kann der Python-Speichermanager unter bestimmten Umständen geeignete Maßnahmen wie Garbage Collection, Speicherkompaktierung oder andere präventive Verfahren auslösen oder auch nicht. Beachten Sie, dass bei Verwendung des C-Bibliotheksallokators, wie im vorherigen Beispiel gezeigt, der zugewiesene Speicher für den E/A-Puffer vollständig aus dem Python-Speichermanager entkommt.

Siehe auch

Die Umgebungsvariable PYTHONMALLOC kann verwendet werden, um die von Python verwendeten Speicherallokatoren zu konfigurieren.

Die Umgebungsvariable PYTHONMALLOCSTATS kann verwendet werden, um Statistiken des pymalloc Speicherallokators jedes Mal auszugeben, wenn eine neue pymalloc-Objekt-Arena erstellt wird, sowie beim Herunterfahren.

Allokator-Domänen

Alle Allokationsfunktionen gehören zu einer von drei verschiedenen „Domänen“ (siehe auch PyMemAllocatorDomain). Diese Domänen repräsentieren unterschiedliche Allokationsstrategien und sind für verschiedene Zwecke optimiert. Die spezifischen Details, wie jede Domäne Speicher zuweist oder welche internen Funktionen jede Domäne aufruft, gelten als Implementierungsdetails. Zu Debugging-Zwecken findet sich eine vereinfachte Tabelle unter hier. Die APIs zum Zuweisen und Freigeben eines Speicherblocks müssen aus derselben Domäne stammen. Beispielsweise muss PyMem_Free() verwendet werden, um mit PyMem_Malloc() zugewiesenen Speicher freizugeben.

Die drei Allokationsdomänen sind

  • Rohdomäne: Dient zur Zuweisung von Speicher für allgemeine Speicherpuffer, bei denen die Zuweisung *unbedingt* an den Systemallokator gehen muss oder bei denen der Allokator ohne einen angehängten Thread-Zustand arbeiten kann. Der Speicher wird direkt vom System angefordert. Siehe Roh-Speicher-Schnittstelle.

  • „Mem“-Domäne: Dient zur Zuweisung von Speicher für Python-Puffer und allgemeine Speicherpuffer, bei denen die Zuweisung mit einem angehängten Thread-Zustand erfolgen muss. Der Speicher wird aus dem privaten Python-Heap bezogen. Siehe Speicher-Schnittstelle.

  • Objektdomäne: Dient zur Zuweisung von Speicher für Python-Objekte. Der Speicher wird aus dem privaten Python-Heap bezogen. Siehe Objekt-Allokatoren.

Hinweis

Der freithread-fähige Build erfordert, dass nur Python-Objekte über die „Objekt“-Domäne zugewiesen werden und dass alle Python-Objekte über diese Domäne zugewiesen werden. Dies unterscheidet sich von früheren Python-Versionen, wo dies nur eine bewährte Methode und keine zwingende Anforderung war.

Beispielsweise sollten Puffer (nicht-Python-Objekte) mit PyMem_Malloc(), PyMem_RawMalloc() oder malloc() zugewiesen werden, aber nicht mit PyObject_Malloc().

Siehe Speicher-API-Funktionen.

Roh-Speicher-Schnittstelle

Die folgenden Funktionssätze sind Wrapper für den Systemallokator. Diese Funktionen sind threadsicher, sodass kein Thread-Zustand angehängt werden muss.

Der Standard-Rohspeicherallokator verwendet die folgenden Funktionen: malloc(), calloc(), realloc() und free(); rufen Sie malloc(1) (oder calloc(1, 1)) auf, wenn Sie null Bytes anfordern.

Hinzugefügt in Version 3.4.

void *PyMem_RawMalloc(size_t n)
Teil des Stable ABI seit Version 3.13.

Weist n Bytes zu und gibt einen Zeiger vom Typ void* auf den zugewiesenen Speicher zurück, oder NULL, wenn die Anforderung fehlschlägt.

Die Anforderung von null Bytes gibt einen eindeutigen Nicht-NULL-Zeiger zurück, wenn möglich, so als wäre PyMem_RawMalloc(1) aufgerufen worden. Der Speicher wurde in keiner Weise initialisiert.

void *PyMem_RawCalloc(size_t nelem, size_t elsize)
Teil des Stable ABI seit Version 3.13.

Weist nelem Elemente zu, die jeweils eine Größe von elsize Bytes haben, und gibt einen Zeiger vom Typ void* auf den zugewiesenen Speicher zurück, oder NULL, wenn die Anforderung fehlschlägt. Der Speicher wird mit Nullen initialisiert.

Die Anforderung von null Elementen oder Elementen mit null Bytes Größe gibt einen eindeutigen Nicht-NULL-Zeiger zurück, wenn möglich, so als wäre PyMem_RawCalloc(1, 1) aufgerufen worden.

Hinzugefügt in Version 3.5.

void *PyMem_RawRealloc(void *p, size_t n)
Teil des Stable ABI seit Version 3.13.

Ändert die Größe des von p referenzierten Speicherblocks auf n Bytes. Der Inhalt bleibt bis zum Minimum der alten und der neuen Größe unverändert.

Wenn p NULL ist, ist der Aufruf äquivalent zu PyMem_RawMalloc(n); wenn n gleich null ist, wird die Größe des Speicherblocks geändert, aber nicht freigegeben, und der zurückgegebene Zeiger ist nicht NULL.

Sofern p nicht NULL ist, muss er von einem vorherigen Aufruf von PyMem_RawMalloc(), PyMem_RawRealloc() oder PyMem_RawCalloc() zurückgegeben worden sein.

Wenn die Anforderung fehlschlägt, gibt PyMem_RawRealloc() NULL zurück und p bleibt ein gültiger Zeiger auf den vorherigen Speicherbereich.

void PyMem_RawFree(void *p)
Teil des Stable ABI seit Version 3.13.

Gibt den von p referenzierten Speicherblock frei, der von einem vorherigen Aufruf von PyMem_RawMalloc(), PyMem_RawRealloc() oder PyMem_RawCalloc() zurückgegeben worden sein muss. Andernfalls oder wenn PyMem_RawFree(p) zuvor aufgerufen wurde, tritt undefiniertes Verhalten auf.

Wenn p NULL ist, wird keine Operation durchgeführt.

Speicher-Schnittstelle

Die folgenden Funktionssätze, die der ANSI-C-Norm nachempfunden sind, aber das Verhalten bei Anforderung von null Bytes spezifizieren, stehen zur Zuweisung und Freigabe von Speicher aus dem Python-Heap zur Verfügung.

Der Standard-Speicherallokator verwendet den pymalloc Speicherallokator.

Warnung

Es muss ein angehängter Thread-Zustand vorhanden sein, wenn diese Funktionen verwendet werden.

Geändert in Version 3.6: Der Standard-Allokator ist jetzt pymalloc anstelle des System-malloc().

void *PyMem_Malloc(size_t n)
Teil der Stable ABI.

Weist n Bytes zu und gibt einen Zeiger vom Typ void* auf den zugewiesenen Speicher zurück, oder NULL, wenn die Anforderung fehlschlägt.

Die Anforderung von null Bytes gibt einen eindeutigen Nicht-NULL-Zeiger zurück, wenn möglich, so als wäre PyMem_Malloc(1) aufgerufen worden. Der Speicher wurde in keiner Weise initialisiert.

void *PyMem_Calloc(size_t nelem, size_t elsize)
Teil der Stable ABI seit Version 3.7.

Weist nelem Elemente zu, die jeweils eine Größe von elsize Bytes haben, und gibt einen Zeiger vom Typ void* auf den zugewiesenen Speicher zurück, oder NULL, wenn die Anforderung fehlschlägt. Der Speicher wird mit Nullen initialisiert.

Die Anforderung von null Elementen oder Elementen mit null Bytes Größe gibt einen eindeutigen Nicht-NULL-Zeiger zurück, wenn möglich, so als wäre PyMem_Calloc(1, 1) aufgerufen worden.

Hinzugefügt in Version 3.5.

void *PyMem_Realloc(void *p, size_t n)
Teil der Stable ABI.

Ändert die Größe des von p referenzierten Speicherblocks auf n Bytes. Der Inhalt bleibt bis zum Minimum der alten und der neuen Größe unverändert.

Wenn p NULL ist, ist der Aufruf äquivalent zu PyMem_Malloc(n); wenn n gleich null ist, wird die Größe des Speicherblocks geändert, aber nicht freigegeben, und der zurückgegebene Zeiger ist nicht NULL.

Sofern p nicht NULL ist, muss er von einem vorherigen Aufruf von PyMem_Malloc(), PyMem_Realloc() oder PyMem_Calloc() zurückgegeben worden sein.

Wenn die Anforderung fehlschlägt, gibt PyMem_Realloc() NULL zurück und p bleibt ein gültiger Zeiger auf den vorherigen Speicherbereich.

void PyMem_Free(void *p)
Teil der Stable ABI.

Gibt den von p referenzierten Speicherblock frei, der von einem vorherigen Aufruf von PyMem_Malloc(), PyMem_Realloc() oder PyMem_Calloc() zurückgegeben worden sein muss. Andernfalls oder wenn PyMem_Free(p) zuvor aufgerufen wurde, tritt undefiniertes Verhalten auf.

Wenn p NULL ist, wird keine Operation durchgeführt.

Die folgenden typorientierten Makros sind zur Bequemlichkeit vorgesehen. Beachten Sie, dass TYPE sich auf jeden C-Typ bezieht.

PyMem_New(TYPE, n)

Gleichbedeutend mit PyMem_Malloc(), weist aber (n * sizeof(TYPE)) Bytes Speicher zu. Gibt einen Zeiger zurück, der auf TYPE* gecastet ist. Der Speicher wird in keiner Weise initialisiert.

PyMem_Resize(p, TYPE, n)

Gleichbedeutend mit PyMem_Realloc(), aber die Größe des Speicherblocks wird auf (n * sizeof(TYPE)) Bytes geändert. Gibt einen Zeiger zurück, der auf TYPE* gecastet ist. Nach der Rückgabe wird p ein Zeiger auf den neuen Speicherbereich sein oder NULL im Fehlerfall.

Dies ist ein C-Präprozessor-Makro; p wird immer neu zugewiesen. Speichern Sie den ursprünglichen Wert von p, um bei der Fehlerbehandlung keinen Speicher zu verlieren.

void PyMem_Del(void *p)

Gleichbedeutend mit PyMem_Free().

Zusätzlich werden die folgenden Makrosätze zur Verfügung gestellt, um den Python-Speicherallokator direkt aufzurufen, ohne die oben genannten C-API-Funktionen einzubeziehen. Beachten Sie jedoch, dass ihre Verwendung keine binäre Kompatibilität über Python-Versionen hinweg gewährleistet und daher in Erweiterungsmodulen als veraltet gilt.

  • PyMem_MALLOC(size)

  • PyMem_NEW(type, size)

  • PyMem_REALLOC(ptr, size)

  • PyMem_RESIZE(ptr, type, size)

  • PyMem_FREE(ptr)

  • PyMem_DEL(ptr)

Objekt-Allokatoren

Die folgenden Funktionssätze, die der ANSI-C-Norm nachempfunden sind, aber das Verhalten bei Anforderung von null Bytes spezifizieren, stehen zur Zuweisung und Freigabe von Speicher aus dem Python-Heap zur Verfügung.

Hinweis

Es gibt keine Garantie, dass der von diesen Allokatoren zurückgegebene Speicher erfolgreich in ein Python-Objekt gecastet werden kann, wenn die zuweisenden Funktionen in dieser Domäne mit den in der Sektion Speicher-Allokatoren anpassen beschriebenen Methoden abgefangen werden.

Der Standard-Objekt-Allokator verwendet den pymalloc Speicherallokator.

Warnung

Es muss ein angehängter Thread-Zustand vorhanden sein, wenn diese Funktionen verwendet werden.

void *PyObject_Malloc(size_t n)
Teil der Stable ABI.

Weist n Bytes zu und gibt einen Zeiger vom Typ void* auf den zugewiesenen Speicher zurück, oder NULL, wenn die Anforderung fehlschlägt.

Die Anforderung von null Bytes gibt einen eindeutigen Nicht-NULL-Zeiger zurück, wenn möglich, so als wäre PyObject_Malloc(1) aufgerufen worden. Der Speicher wurde in keiner Weise initialisiert.

void *PyObject_Calloc(size_t nelem, size_t elsize)
Teil der Stable ABI seit Version 3.7.

Weist nelem Elemente zu, die jeweils eine Größe von elsize Bytes haben, und gibt einen Zeiger vom Typ void* auf den zugewiesenen Speicher zurück, oder NULL, wenn die Anforderung fehlschlägt. Der Speicher wird mit Nullen initialisiert.

Die Anforderung von null Elementen oder Elementen mit null Bytes Größe gibt einen eindeutigen Nicht-NULL-Zeiger zurück, wenn möglich, so als wäre PyObject_Calloc(1, 1) aufgerufen worden.

Hinzugefügt in Version 3.5.

void *PyObject_Realloc(void *p, size_t n)
Teil der Stable ABI.

Ändert die Größe des von p referenzierten Speicherblocks auf n Bytes. Der Inhalt bleibt bis zum Minimum der alten und der neuen Größe unverändert.

Wenn p NULL ist, ist der Aufruf äquivalent zu PyObject_Malloc(n); wenn n gleich null ist, wird die Größe des Speicherblocks geändert, aber nicht freigegeben, und der zurückgegebene Zeiger ist nicht NULL.

Sofern p nicht NULL ist, muss er von einem vorherigen Aufruf von PyObject_Malloc(), PyObject_Realloc() oder PyObject_Calloc() zurückgegeben worden sein.

Wenn die Anforderung fehlschlägt, gibt PyObject_Realloc() NULL zurück und p bleibt ein gültiger Zeiger auf den vorherigen Speicherbereich.

void PyObject_Free(void *p)
Teil der Stable ABI.

Gibt den von p referenzierten Speicherblock frei, der von einem vorherigen Aufruf von PyObject_Malloc(), PyObject_Realloc() oder PyObject_Calloc() zurückgegeben worden sein muss. Andernfalls oder wenn PyObject_Free(p) zuvor aufgerufen wurde, tritt undefiniertes Verhalten auf.

Wenn p NULL ist, wird keine Operation durchgeführt.

Rufen Sie diese Funktion nicht direkt auf, um den Speicher eines Objekts freizugeben. Rufen Sie stattdessen den tp_free Slot des Typs auf.

Verwenden Sie diese Funktion nicht für Speicher, der von PyObject_GC_New oder PyObject_GC_NewVar zugewiesen wurde; verwenden Sie stattdessen PyObject_GC_Del().

Siehe auch

Standard-Speicherallokatoren

Standard-Speicherallokatoren

Konfiguration

Name

PyMem_RawMalloc

PyMem_Malloc

PyObject_Malloc

Release Build

"pymalloc"

malloc

pymalloc

pymalloc

Debug Build

"pymalloc_debug"

malloc + Debug

pymalloc + Debug

pymalloc + Debug

Release Build, ohne pymalloc

"malloc"

malloc

malloc

malloc

Debug Build, ohne pymalloc

"malloc_debug"

malloc + Debug

malloc + Debug

malloc + Debug

Legende

Speicher-Allokatoren anpassen

Hinzugefügt in Version 3.4.

type PyMemAllocatorEx

Struktur, die zur Beschreibung eines Speicherblock-Allokators verwendet wird. Die Struktur hat die folgenden Felder

Feld

Bedeutung

void *ctx

Benutzerkontext, der als erstes Argument übergeben wird

void* malloc(void *ctx, size_t size)

Speicherblock zuweisen

void* calloc(void *ctx, size_t nelem, size_t elsize)

Speicherblock zuweisen, der mit Nullen initialisiert ist

void* realloc(void *ctx, void *ptr, size_t new_size)

Speicherblock zuweisen oder Größe ändern

void free(void *ctx, void *ptr)

Speicherblock freigeben

Geändert in Version 3.5: Die PyMemAllocator Struktur wurde in PyMemAllocatorEx umbenannt und ein neues calloc Feld wurde hinzugefügt.

type PyMemAllocatorDomain

Enum zur Identifizierung einer Allocator-Domäne. Domänen

PYMEM_DOMAIN_RAW

Funktionen

PYMEM_DOMAIN_MEM

Funktionen

PYMEM_DOMAIN_OBJ

Funktionen

void PyMem_GetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator)

Holt den Speicherblock-Allocator der angegebenen Domäne.

void PyMem_SetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator)

Setzt den Speicherblock-Allocator der angegebenen Domäne.

Der neue Allocator muss einen eindeutigen Nicht-NULL-Zeiger zurückgeben, wenn null Bytes angefordert werden.

Für die PYMEM_DOMAIN_RAW Domäne muss der Allocator Thread-sicher sein: Ein Thread-Zustand ist nicht angehängt, wenn der Allocator aufgerufen wird.

Für die restlichen Domänen muss der Allocator ebenfalls Thread-sicher sein: Der Allocator kann in verschiedenen Interpretern aufgerufen werden, die keine GIL teilen.

Wenn der neue Allocator kein Hook ist (ruft nicht den vorherigen Allocator auf), muss die Funktion PyMem_SetupDebugHooks() aufgerufen werden, um die Debug-Hooks auf dem neuen Allocator neu zu installieren.

Siehe auch PyPreConfig.allocator und Python vorinitialisieren mit PyPreConfig.

Warnung

PyMem_SetAllocator() hat den folgenden Vertrag

  • Er kann nach Py_PreInitialize() und vor Py_InitializeFromConfig() aufgerufen werden, um einen benutzerdefinierten Speicher-Allocator zu installieren. Es gibt keine Beschränkungen für den installierten Allocator, außer denen, die von der Domäne auferlegt werden (z. B. erlaubt die Raw-Domäne, dass der Allocator ohne angehängten Thread-Zustand aufgerufen wird). Weitere Informationen finden Sie in dem Abschnitt über Allocator-Domänen.

  • Wenn er nach Abschluss der Initialisierung von Python aufgerufen wird (nachdem Py_InitializeFromConfig() aufgerufen wurde), muss der Allocator den bestehenden Allocator *umhüllen*. Das Ersetzen des aktuellen Allocators durch einen beliebigen anderen ist *nicht unterstützt*.

Geändert in Version 3.12: Alle Allocatoren müssen Thread-sicher sein.

void PyMem_SetupDebugHooks(void)

Richtet Debug-Hooks in den Python-Speicherallokatoren ein, um Speicherfehler zu erkennen.

Debug-Hooks für die Speicherallokatoren von Python

Wenn Python im Debug-Modus kompiliert wird, wird die Funktion PyMem_SetupDebugHooks() während der Python-Vorinitialisierung aufgerufen, um Debug-Hooks in den Python-Speicherallokatoren einzurichten und Speicherfehler zu erkennen.

Die Umgebungsvariable PYTHONMALLOC kann verwendet werden, um Debug-Hooks in einem in Release-Modus kompilierten Python zu installieren (z. B. PYTHONMALLOC=debug).

Die Funktion PyMem_SetupDebugHooks() kann verwendet werden, um Debug-Hooks nach dem Aufruf von PyMem_SetAllocator() zu setzen.

Diese Debug-Hooks füllen dynamisch zugewiesene Speicherblöcke mit speziellen, erkennbaren Bitmustern. Neu zugewiesener Speicher wird mit dem Byte 0xCD (PYMEM_CLEANBYTE) gefüllt, freigegebener Speicher wird mit dem Byte 0xDD (PYMEM_DEADBYTE) gefüllt. Speicherblöcke werden von "verbotenen Bytes" umgeben, die mit dem Byte 0xFD (PYMEM_FORBIDDENBYTE) gefüllt sind. Zeichenketten dieser Bytes sind unwahrscheinlich gültige Adressen, Gleitkommazahlen oder ASCII-Zeichenketten.

Laufzeitprüfungen

Im Fehlerfall verwenden die Debug-Hooks das Modul tracemalloc, um den Traceback zu erhalten, wo ein Speicherblock zugewiesen wurde. Der Traceback wird nur angezeigt, wenn tracemalloc Python-Speicherzuweisungen verfolgt und der Speicherblock verfolgt wurde.

Sei *S* = sizeof(size_t). 2*S Bytes werden an jedem Ende jedes Blocks von *N* angeforderten Bytes hinzugefügt. Das Speicherlayout sieht wie folgt aus, wobei p die von einer malloc-ähnlichen oder realloc-ähnlichen Funktion zurückgegebene Adresse darstellt (p[i:j] bedeutet den Slice von Bytes von *(p+i) inklusive bis *(p+j) exklusiv; beachte, dass die Behandlung negativer Indizes von einem Python-Slice abweicht)

p[-2*S:-S]

Anzahl der ursprünglich angeforderten Bytes. Dies ist ein size_t, Big-Endian (leichter in einem Speicherauszug zu lesen).

p[-S]

API-Identifikator (ASCII-Zeichen)

p[-S+1:0]

Kopien von PYMEM_FORBIDDENBYTE. Wird verwendet, um Unterlauf-Schreib- und Lesevorgänge zu erkennen.

p[0:N]

Der angeforderte Speicher, gefüllt mit Kopien von PYMEM_CLEANBYTE, um Verweise auf nicht initialisierten Speicher zu erkennen. Wenn eine realloc-ähnliche Funktion aufgerufen wird, um einen größeren Speicherblock anzufordern, werden die neuen überschüssigen Bytes ebenfalls mit PYMEM_CLEANBYTE gefüllt. Wenn eine free-ähnliche Funktion aufgerufen wird, werden diese mit PYMEM_DEADBYTE überschrieben, um Verweise auf freigegebenen Speicher zu erkennen. Wenn eine realloc-ähnliche Funktion aufgerufen wird, um einen kleineren Speicherblock anzufordern, werden die überschüssigen alten Bytes ebenfalls mit PYMEM_DEADBYTE gefüllt.

p[N:N+S]

Kopien von PYMEM_FORBIDDENBYTE. Wird verwendet, um Überlauf-Schreib- und Lesevorgänge zu erkennen.

p[N+S:N+2*S]

Nur verwendet, wenn das Makro PYMEM_DEBUG_SERIALNO definiert ist (standardmäßig nicht definiert).

Eine fortlaufende Nummer, die bei jedem Aufruf einer malloc-ähnlichen oder realloc-ähnlichen Funktion um 1 erhöht wird. Big-Endian size_t. Wenn später "schlechter Speicher" erkannt wird, gibt die Seriennummer eine hervorragende Möglichkeit, beim nächsten Durchlauf einen Breakpoint zu setzen, um den Moment zu erfassen, in dem dieser Block ausgegeben wurde. Die statische Funktion bumpserialno() in obmalloc.c ist der einzige Ort, an dem die Seriennummer erhöht wird, und existiert, damit Sie einen solchen Breakpoint leicht setzen können.

Eine realloc-ähnliche oder free-ähnliche Funktion prüft zuerst, ob die PYMEM_FORBIDDENBYTE-Bytes an jedem Ende intakt sind. Wenn sie verändert wurden, werden Diagnoseausgaben nach stderr geschrieben und das Programm wird über Py_FatalError() abgebrochen. Der andere Hauptfehlerfall ist das Auslösen eines Speicherfehlers, wenn ein Programm einen der speziellen Bitmuster liest und versucht, ihn als Adresse zu verwenden. Wenn Sie dann in einen Debugger gehen und das Objekt betrachten, werden Sie wahrscheinlich sehen, dass es vollständig mit PYMEM_DEADBYTE gefüllt ist (was bedeutet, dass freigegebener Speicher verwendet wird) oder PYMEM_CLEANBYTE (was bedeutet, dass uninitialisierter Speicher verwendet wird).

Geändert in Version 3.6: Die Funktion PyMem_SetupDebugHooks() funktioniert jetzt auch auf in Release-Modus kompiliertem Python. Im Fehlerfall verwenden die Debug-Hooks nun tracemalloc, um den Traceback zu erhalten, wo ein Speicherblock zugewiesen wurde. Die Debug-Hooks prüfen nun auch, ob ein angehängter Thread-Zustand vorhanden ist, wenn Funktionen der Domänen PYMEM_DOMAIN_OBJ und PYMEM_DOMAIN_MEM aufgerufen werden.

Geändert in Version 3.8: Die Byte-Muster 0xCB (PYMEM_CLEANBYTE), 0xDB (PYMEM_DEADBYTE) und 0xFB (PYMEM_FORBIDDENBYTE) wurden durch 0xCD, 0xDD und 0xFD ersetzt, um die gleichen Werte wie bei den Windows CRT Debug malloc() und free() zu verwenden.

Der Pymalloc-Allocator

Python verfügt über einen *pymalloc*-Allocator, der für kleine Objekte (kleiner oder gleich 512 Bytes) mit kurzer Lebensdauer optimiert ist. Er verwendet Speicherabbilder, die als „Arenen“ bezeichnet werden und eine feste Größe von entweder 256 KiB auf 32-Bit-Plattformen oder 1 MiB auf 64-Bit-Plattformen haben. Für Zuweisungen größer als 512 Bytes greift er auf PyMem_RawMalloc() und PyMem_RawRealloc() zurück.

pymalloc ist der Standard-Allocator der Domänen PYMEM_DOMAIN_MEM (z. B. PyMem_Malloc()) und PYMEM_DOMAIN_OBJ (z. B. PyObject_Malloc()).

Der Arena-Allocator verwendet die folgenden Funktionen

  • VirtualAlloc() und VirtualFree() unter Windows,

  • mmap() und munmap(), falls verfügbar,

  • malloc() und free() andernfalls.

Dieser Allocator ist deaktiviert, wenn Python mit der Option --without-pymalloc konfiguriert ist. Er kann auch zur Laufzeit über die Umgebungsvariable PYTHONMALLOC deaktiviert werden (z. B. PYTHONMALLOC=malloc).

Typischerweise ist es sinnvoll, den pymalloc-Allocator zu deaktivieren, wenn Python mit AddressSanitizer (--with-address-sanitizer) kompiliert wird, was bei der Aufdeckung von Low-Level-Fehlern im C-Code hilft.

Benutzerdefinierten Arena-Allocator für Pymalloc anpassen

Hinzugefügt in Version 3.4.

type PyObjectArenaAllocator

Struktur zur Beschreibung eines Arena-Allocators. Die Struktur hat drei Felder

Feld

Bedeutung

void *ctx

Benutzerkontext, der als erstes Argument übergeben wird

void* alloc(void *ctx, size_t size)

einen Arena von Größe Bytes zuweisen

void free(void *ctx, void *ptr, size_t size)

einen Arena freigeben

void PyObject_GetArenaAllocator(PyObjectArenaAllocator *allocator)

Den Arena-Allocator erhalten.

void PyObject_SetArenaAllocator(PyObjectArenaAllocator *allocator)

Den Arena-Allocator setzen.

Der mimalloc-Allocator

Hinzugefügt in Version 3.13.

Python unterstützt den mimalloc-Allocator, wenn die zugrunde liegende Plattformunterstützung verfügbar ist. mimalloc "ist ein Allzweck-Allocator mit hervorragenden Leistungseigenschaften. Ursprünglich von Daan Leijen für die Laufzeitsysteme der Koka- und Lean-Sprachen entwickelt."

tracemalloc C API

Hinzugefügt in Version 3.7.

int PyTraceMalloc_Track(unsigned int domain, uintptr_t ptr, size_t size)

Verfolgt einen zugewiesenen Speicherblock im Modul tracemalloc.

Gibt 0 bei Erfolg zurück, gibt -1 bei einem Fehler zurück (fehlgeschlagene Speicherzuweisung zur Speicherung des Traces). Gibt -2 zurück, wenn tracemalloc deaktiviert ist.

Wenn der Speicherblock bereits verfolgt wird, wird der bestehende Trace aktualisiert.

int PyTraceMalloc_Untrack(unsigned int domain, uintptr_t ptr)

Entfernt einen zugewiesenen Speicherblock aus der Verfolgung im Modul tracemalloc. Tut nichts, wenn der Block nicht verfolgt wurde.

Gibt -2 zurück, wenn tracemalloc deaktiviert ist, andernfalls gibt er 0 zurück.

Beispiele

Hier ist das Beispiel aus Abschnitt Überblick, neu geschrieben, so dass der I/O-Puffer aus dem Python-Heap zugewiesen wird, indem die erste Funktionsmenge verwendet wird

PyObject *res;
char *buf = (char *) PyMem_Malloc(BUFSIZ); /* for I/O */

if (buf == NULL)
    return PyErr_NoMemory();
/* ...Do some I/O operation involving buf... */
res = PyBytes_FromString(buf);
PyMem_Free(buf); /* allocated with PyMem_Malloc */
return res;

Der gleiche Code unter Verwendung der typorientierten Funktionsmenge

PyObject *res;
char *buf = PyMem_New(char, BUFSIZ); /* for I/O */

if (buf == NULL)
    return PyErr_NoMemory();
/* ...Do some I/O operation involving buf... */
res = PyBytes_FromString(buf);
PyMem_Free(buf); /* allocated with PyMem_New */
return res;

Beachten Sie, dass in den beiden obigen Beispielen der Puffer immer über Funktionen manipuliert wird, die zur selben Menge gehören. Tatsächlich ist es erforderlich, dieselbe Speicher-API-Familie für einen gegebenen Speicherblock zu verwenden, so dass das Risiko, verschiedene Allokatoren zu mischen, auf ein Minimum reduziert wird. Die folgende Code-Sequenz enthält zwei Fehler, von denen einer als *fatal* gekennzeichnet ist, da er zwei verschiedene Allokatoren mischt, die auf unterschiedlichen Heaps arbeiten.

char *buf1 = PyMem_New(char, BUFSIZ);
char *buf2 = (char *) malloc(BUFSIZ);
char *buf3 = (char *) PyMem_Malloc(BUFSIZ);
...
PyMem_Del(buf3);  /* Wrong -- should be PyMem_Free() */
free(buf2);       /* Right -- allocated via malloc() */
free(buf1);       /* Fatal -- should be PyMem_Free()  */

Zusätzlich zu den Funktionen zur Handhabung von Rohspeicherblöcken aus dem Python-Heap werden Objekte in Python mit PyObject_New, PyObject_NewVar und PyObject_Free() zugewiesen und freigegeben.

Diese werden im nächsten Kapitel über das Definieren und Implementieren neuer Objekttypen in C erläutert.