Im Allgemeinen müssen Sie sich beim Schreiben von Python-Code keine Gedanken über den Garbage Collector und die Arbeit mit dem Speicher machen. Sobald Objekte nicht mehr benötigt werden, gibt Python automatisch Speicher unter ihnen frei. Wenn Sie jedoch verstehen, wie GC funktioniert, können Sie besseren Code schreiben.
Speichermanager
Im Gegensatz zu anderen gängigen Sprachen gibt Python nicht den gesamten Speicher an das Betriebssystem zurück, sobald ein Objekt gelöscht wird. Stattdessen wird ein zusätzlicher Speichermanager für kleine Objekte verwendet (deren Größe weniger als 512 Byte beträgt). Um mit solchen Objekten arbeiten zu können, weist er große Speicherblöcke zu, in denen in Zukunft viele kleine Objekte gespeichert werden.
Sobald eines der kleinen Objekte gelöscht wird - der Speicher darunter geht nicht an das Betriebssystem, Python lässt es für neue Objekte mit der gleichen Größe. Wenn in einem der zugewiesenen Speicherblöcke keine Objekte mehr vorhanden sind, kann Python diese für das Betriebssystem freigeben. In der Regel werden Blöcke freigegeben, wenn das Skript viele temporäre Objekte erstellt.
Wenn ein langlebiger Python-Prozess im Laufe der Zeit mehr Speicher verbraucht, bedeutet dies keinesfalls, dass Ihr Code ein Problem mit Speicherverlusten aufweist. Wenn Sie mehr über den Speichermanager in Python erfahren möchten, können Sie dies in
meinem anderen Artikel nachlesen.
Garbage Collection-Algorithmen
Der Standard-Python-Interpreter (CPython) verwendet zwei Algorithmen gleichzeitig: Referenzzählung und Generations-Garbage-Collector (im Folgenden: GC), besser bekannt als das Standard-
GC-Modul von Python.
Der Link-Counting-Algorithmus ist sehr einfach und effizient, hat jedoch einen großen Nachteil. Er weiß nicht, wie man Zirkelverweise definiert. Aus diesem Grund gibt es in Python einen zusätzlichen Kollektor namens Generation GC, der Objekte mit potenziellen Zirkelreferenzen überwacht.
In Python ist der Referenzzählalgorithmus grundlegend und kann nicht deaktiviert werden, während der GC optional ist und deaktiviert werden kann.
Link Counting Algorithmus
Der Link-Counting-Algorithmus ist eine der einfachsten Techniken zur Speicherbereinigung. Objekte werden gelöscht, sobald sie nicht mehr referenziert werden.
In Python speichern Variablen keine Werte, sondern dienen als Verweise auf Objekte. Das heißt, wenn Sie einer neuen Variablen einen Wert zuweisen, wird zuerst ein Objekt mit diesem Wert erstellt, und erst dann beginnt die Variable, darauf zu verweisen. Mehrere Variablen können auf ein einzelnes Objekt verweisen.
Jedes Objekt in Python enthält ein zusätzliches Feld (Referenzzähler), in dem die Anzahl der Links zu diesem Objekt gespeichert ist. Sobald sich jemand auf ein Objekt bezieht, wird dieses Feld um eins erhöht. Wenn der Link aus irgendeinem Grund verschwindet, wird dieses Feld um eins reduziert.
Beispiele, wenn die Anzahl der Links zunimmt:
- Zuweisungsoperator
- Argumente übergeben
- Fügen Sie ein neues Objekt in das Blatt ein (die Anzahl der Links für das Objekt erhöht sich).
- eine Konstruktion der Form foo = bar (foo beginnt sich auf dasselbe Objekt wie bar zu beziehen)
Sobald der Referenzzähler für ein bestimmtes Objekt Null erreicht, beginnt der Interpreter mit der Zerstörung des Objekts. Wenn das entfernte Objekt Links zu anderen Objekten enthielt, werden diese Links ebenfalls gelöscht. Somit kann das Entfernen eines Objekts das Entfernen anderer Objekte beinhalten.
Wenn beispielsweise eine Liste gelöscht wird, wird die Referenzanzahl in allen Elementen um eins reduziert. Wenn alle Objekte in der Liste nirgendwo anders verwendet werden, werden sie ebenfalls gelöscht.
Variablen, die außerhalb von Funktionen, Klassen und Blöcken deklariert sind, werden als global bezeichnet. In der Regel entspricht der Lebenszyklus solcher Variablen der Lebensdauer des Python-Prozesses. Daher fällt die Anzahl der Verweise auf Objekte, auf die durch globale Variablen verwiesen wird, niemals auf Null.
Variablen, die innerhalb eines Blocks (Funktion, Klasse) deklariert sind, sind lokal sichtbar (d. H. Sie sind nur innerhalb des Blocks sichtbar). Sobald der Python-Interpreter den Block verlässt, werden alle durch lokale Variablen in ihm erstellten Links zerstört.
Sie können die Anzahl der Links
sys.getrefcount
mit der Funktion
sys.getrefcount
überprüfen.
Beispiel eines Linkzählers:
foo = []
Der Hauptgrund, warum der Standardinterpreter (CPython) einen Referenzzähler verwendet, ist historisch. Über diesen Ansatz wird derzeit viel diskutiert. Einige Leute glauben, dass ein Garbage Collector ohne einen Link-Counting-Algorithmus viel effizienter sein kann. Dieser Algorithmus weist viele Probleme auf, z. B. zirkuläre Links, blockierende Threads sowie zusätzlichen Overhead für Speicher und CPU.
Der Hauptvorteil dieses Algorithmus besteht darin, dass Objekte sofort gelöscht werden, sobald sie nicht benötigt werden.
Optionaler Müllsammler
Warum brauchen wir einen zusätzlichen Algorithmus, wenn wir bereits Referenzzählung haben?
Leider hat der klassische Linkzählalgorithmus einen großen Nachteil: Er weiß nicht, wie man kreisförmige Links findet. Loopbacks treten auf, wenn ein oder mehrere Objekte aufeinander verweisen.
Zwei Beispiele:

Wie Sie sehen können, bezieht sich das
lst
Objekt auf sich selbst, während sich
object1
und
object2
beziehen. Für solche Objekte beträgt der Referenzzähler immer 1.
Python-Demo:
import gc
Im obigen Beispiel entfernt die Anweisung del Verweise auf unsere Objekte (nicht auf die Objekte selbst). Sobald Python eine del-Anweisung ausführt, kann über Python-Code nicht mehr auf diese Objekte zugegriffen werden. Wenn das GC-Modul ausgeschaltet ist, bleiben sie jedoch weiterhin im Speicher Sie hatten Zirkelverweise und ihr Zähler ist immer noch einer. Sie können solche Beziehungen mithilfe der
objgraph
visuell
objgraph
.
Um dieses Problem zu beheben, wurde in Python 1.5 ein zusätzlicher Algorithmus hinzugefügt, der als
gc-Modul bekannt ist . Die einzige Aufgabe besteht darin, zyklische Objekte zu entfernen, auf die aus dem Code kein Zugriff mehr besteht.
Loopbacks können nur in Containerobjekten auftreten. Das heißt, in Objekten, in denen andere Objekte gespeichert werden können, z. B. Listen, Wörterbücher, Klassen und Tupel. GC verfolgt keine einfachen und unveränderlichen Typen, außer Tupeln. Einige Tupel und Wörterbücher werden ebenfalls von der Verfolgungsliste ausgeschlossen, wenn bestimmte Bedingungen erfüllt sind. Bei allen anderen Objekten wird garantiert, dass der Referenzzählalgorithmus zurechtkommt.
Wenn der GC ausgelöst wird
Im Gegensatz zum Referenzzählalgorithmus arbeitet der zyklische GC nicht in Echtzeit und wird regelmäßig ausgeführt. Jeder Lauf des Kollektors erzeugt Mikropausen im Code, sodass CPython (der Standardinterpreter) verschiedene Heuristiken verwendet, um die Häufigkeit des Garbage Collectors zu bestimmen.
Der zyklische Garbage Collector unterteilt alle Objekte in 3 Generationen (Generationen). Neue Objekte fallen in die erste Generation. Wenn die neue Einrichtung den Speicherbereinigungsprozess überlebt, wechselt sie zur nächsten Generation. Je höher die Generation, desto seltener wird nach Müll gesucht. Da neue Objekte häufig eine sehr kurze Lebensdauer haben (vorübergehend sind), ist es sinnvoll, sie häufiger zu befragen als solche, die bereits mehrere Phasen der Speicherbereinigung durchlaufen haben.
Jede Generation hat einen speziellen Zähler und einen Antwortschwellenwert, bei dessen Erreichen der Speicherbereinigungsprozess ausgelöst wird. Jeder Zähler speichert die Anzahl der Zuweisungen abzüglich der Anzahl der Freigaben in einer bestimmten Generation. Sobald ein Containerobjekt in Python erstellt wurde, werden diese Zähler überprüft. Wenn die Bedingungen funktionieren, beginnt der Speicherbereinigungsprozess.
Wenn mehrere oder mehr Generationen gleichzeitig die Schwelle überschritten haben, wird die älteste Generation ausgewählt. Dies liegt daran, dass ältere Generationen auch alle vorherigen scannen. Um die Anzahl der Speicherbereinigungspausen für langlebige Objekte zu verringern, verfügt die älteste Generation über
zusätzliche Bedingungen .
Die Standardschwellenwerte für Generationen sind auf
700, 10 10
Sie können sie jedoch jederzeit mit den
gc.get_threshold gc.set_threshold
.
Schleifensuchalgorithmus
Für eine vollständige Beschreibung des Schleifensuchalgorithmus ist ein separater Artikel erforderlich. Kurz gesagt, GC iteriert über jedes Objekt der ausgewählten Generationen und entfernt vorübergehend alle Verknüpfungen von einem einzelnen Objekt (alle Verknüpfungen, auf die sich dieses Objekt bezieht). Nach einem vollständigen Durchlauf werden alle Objekte mit einer Linkanzahl von weniger als zwei als von Python nicht zugänglich angesehen und können gelöscht werden.
Für ein tieferes Verständnis empfehle ich, die
ursprüngliche Beschreibung des Algorithmus von Neil Schemenauer und die
collect
aus den
CPython-Quellen zu lesen (Anmerkung des Übersetzers: englisches Material). Eine Beschreibung von
Quora und ein
Beitrag über den Garbage Collector können ebenfalls hilfreich sein.
Es ist erwähnenswert, dass das in der ursprünglichen Beschreibung des Algorithmus beschriebene Problem mit Destruktoren seit Python 3.4 behoben wurde (weitere Details in
PEP 442 ).
Optimierungstipps
Schleifen treten häufig in realen Aufgaben auf, sie können in Problemen mit Diagrammen, verknüpften Listen oder in Datenstrukturen auftreten, in denen Sie die Beziehungen zwischen Objekten verfolgen müssen. Wenn Ihr Programm eine hohe Last hat und Verzögerungen erfordert, sollten Schleifen nach Möglichkeit vermieden werden.
An Stellen, an denen Sie wissentlich zirkuläre Links verwenden, können Sie "schwache" Links verwenden. Schwache Links sind im Schwachstellenmodul implementiert und wirken sich im Gegensatz zu regulären Links in keiner Weise auf den
Linkzähler aus . Wenn sich herausstellt, dass das Objekt mit schwachen Referenzen gelöscht wurde,
None
stattdessen
None
zurückgegeben.
In einigen Fällen ist es hilfreich, die automatische Erstellung durch das GC-Modul zu deaktivieren und manuell aufzurufen. Rufen
gc.disable()
einfach
gc.disable()
und rufen
gc.collect()
dann
gc.collect()
manuell auf.
So finden und debuggen Sie zirkuläre Links
Das Debuggen von Schleifen kann schmerzhaft sein, insbesondere wenn Ihr Code viele Module von Drittanbietern verwendet.
Das gc-Modul bietet Hilfsfunktionen, die beim Debuggen helfen können. Wenn die GC-Parameter auf das Flag
DEBUG_SAVEALL
, werden alle unzugänglichen Objekte zur Liste
gc.garbage
hinzugefügt.
import gc gc.set_debug(gc.DEBUG_SAVEALL) print(gc.get_count()) lst = [] lst.append(lst) list_id = id(lst) del lst gc.collect() for item in gc.garbage: print(item) assert list_id == id(item)
Sobald Sie den Problempunkt identifiziert haben, kann er mit
objgraph visualisiert werden.

Fazit
Der Haupt-Garbage-Collection-Prozess wird von einem Link-Counting-Algorithmus ausgeführt, der sehr einfach ist und keine Einstellungen enthält. Ein zusätzlicher Algorithmus wird nur zum Suchen und Löschen von Objekten mit Zirkelverweisen verwendet.
Sie sollten den Code für den Garbage Collector nicht vorzeitig optimieren. In der Praxis sind Probleme mit der Garbage Collection eher selten.
PS: Ich bin der Autor dieses Artikels, Sie können alle Fragen stellen.