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ärePyMem_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ärePyMem_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
NULList, ist der Aufruf äquivalent zuPyMem_RawMalloc(n); wenn n gleich null ist, wird die Größe des Speicherblocks geändert, aber nicht freigegeben, und der zurückgegebene Zeiger ist nichtNULL.Sofern p nicht
NULList, muss er von einem vorherigen Aufruf vonPyMem_RawMalloc(),PyMem_RawRealloc()oderPyMem_RawCalloc()zurückgegeben worden sein.Wenn die Anforderung fehlschlägt, gibt
PyMem_RawRealloc()NULLzurü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()oderPyMem_RawCalloc()zurückgegeben worden sein muss. Andernfalls oder wennPyMem_RawFree(p)zuvor aufgerufen wurde, tritt undefiniertes Verhalten auf.Wenn p
NULList, 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ärePyMem_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ärePyMem_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
NULList, ist der Aufruf äquivalent zuPyMem_Malloc(n); wenn n gleich null ist, wird die Größe des Speicherblocks geändert, aber nicht freigegeben, und der zurückgegebene Zeiger ist nichtNULL.Sofern p nicht
NULList, muss er von einem vorherigen Aufruf vonPyMem_Malloc(),PyMem_Realloc()oderPyMem_Calloc()zurückgegeben worden sein.Wenn die Anforderung fehlschlägt, gibt
PyMem_Realloc()NULLzurü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()oderPyMem_Calloc()zurückgegeben worden sein muss. Andernfalls oder wennPyMem_Free(p)zuvor aufgerufen wurde, tritt undefiniertes Verhalten auf.Wenn p
NULList, 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 aufTYPE*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 aufTYPE*gecastet ist. Nach der Rückgabe wird p ein Zeiger auf den neuen Speicherbereich sein oderNULLim 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ärePyObject_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ärePyObject_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
NULList, ist der Aufruf äquivalent zuPyObject_Malloc(n); wenn n gleich null ist, wird die Größe des Speicherblocks geändert, aber nicht freigegeben, und der zurückgegebene Zeiger ist nichtNULL.Sofern p nicht
NULList, muss er von einem vorherigen Aufruf vonPyObject_Malloc(),PyObject_Realloc()oderPyObject_Calloc()zurückgegeben worden sein.Wenn die Anforderung fehlschlägt, gibt
PyObject_Realloc()NULLzurü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()oderPyObject_Calloc()zurückgegeben worden sein muss. Andernfalls oder wennPyObject_Free(p)zuvor aufgerufen wurde, tritt undefiniertes Verhalten auf.Wenn p
NULList, wird keine Operation durchgeführt.Rufen Sie diese Funktion nicht direkt auf, um den Speicher eines Objekts freizugeben. Rufen Sie stattdessen den
tp_freeSlot des Typs auf.Verwenden Sie diese Funktion nicht für Speicher, der von
PyObject_GC_NewoderPyObject_GC_NewVarzugewiesen wurde; verwenden Sie stattdessenPyObject_GC_Del().Siehe auch
PyObject_GC_Del()ist das Äquivalent dieser Funktion für Speicher, der von Typen zugewiesen wurde, die Garbage Collection unterstützen.
Standard-Speicherallokatoren¶
Standard-Speicherallokatoren
Konfiguration |
Name |
PyMem_RawMalloc |
PyMem_Malloc |
PyObject_Malloc |
|---|---|---|---|---|
Release Build |
|
|
|
|
Debug Build |
|
|
|
|
Release Build, ohne pymalloc |
|
|
|
|
Debug Build, ohne pymalloc |
|
|
|
|
Legende
Name: Wert für die Umgebungsvariable
PYTHONMALLOC.malloc: Systemallokatoren aus der Standard-C-Bibliothek, C-Funktionen:malloc(),calloc(),realloc()undfree().pymalloc: pymalloc Speicherallokator.mimalloc: mimalloc Speicherallokator. Der pymalloc-Allokator wird verwendet, wenn keine mimalloc-Unterstützung verfügbar ist.„+ Debug“: mit Debug-Hooks für die Python-Speicherallokatoren.
„Debug Build“: Python-Build im Debug-Modus.
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 *ctxBenutzerkontext, 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
PyMemAllocatorStruktur wurde inPyMemAllocatorExumbenannt und ein neuescallocFeld 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
-
PYMEM_DOMAIN_RAW¶
-
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_RAWDomä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.allocatorund Python vorinitialisieren mit PyPreConfig.Warnung
PyMem_SetAllocator()hat den folgenden VertragEr kann nach
Py_PreInitialize()und vorPy_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
Erkennt API-Verletzungen. Erkennt zum Beispiel, ob
PyObject_Free()für einen Speicherblock aufgerufen wird, der vonPyMem_Malloc()zugewiesen wurde.Erkennt Schreibvorgänge vor dem Beginn des Puffers (Buffer-Underflow).
Erkennt Schreibvorgänge nach dem Ende des Puffers (Buffer-Overflow).
Prüft, ob ein angehängter Thread-Zustand vorhanden ist, wenn die Allocator-Funktionen der Domänen
PYMEM_DOMAIN_OBJ(z. B.PyObject_Malloc()) undPYMEM_DOMAIN_MEM(z. B.PyMem_Malloc()) aufgerufen werden.
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)
'r'fürPYMEM_DOMAIN_RAW.'m'fürPYMEM_DOMAIN_MEM.'o'fürPYMEM_DOMAIN_OBJ.
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_SERIALNOdefiniert 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()undVirtualFree()unter Windows,mmap()undmunmap(), falls verfügbar,malloc()undfree()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 *ctxBenutzerkontext, 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
0bei Erfolg zurück, gibt-1bei einem Fehler zurück (fehlgeschlagene Speicherzuweisung zur Speicherung des Traces). Gibt-2zurü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
-2zurück, wenn tracemalloc deaktiviert ist, andernfalls gibt er0zurü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.