Objektlebenszyklus

Dieser Abschnitt erklärt, wie die Slots eines Typs während des Lebens eines Objekts miteinander in Beziehung stehen. Er ist nicht als vollständiges kanonisches Nachschlagewerk für die Slots gedacht; stattdessen finden Sie detailspezifische Informationen zu einem bestimmten Slot in der Slot-spezifischen Dokumentation unter Typobjektstrukturen.

Lebensereignisse

Die folgende Abbildung veranschaulicht die Reihenfolge der Ereignisse, die während des Lebens eines Objekts auftreten können. Ein Pfeil von A nach B zeigt an, dass Ereignis B nach dem Eintreten von Ereignis A auftreten kann, wobei die Beschriftung des Pfeils die Bedingung angibt, die erfüllt sein muss, damit B nach A auftreten kann.

Life Events tp_new tp_new start->tp_new    type call   tp_alloc tp_alloc tp_new->tp_alloc  direct call   tp_init tp_init tp_new->tp_init reachable reachable tp_init->reachable reachable->tp_init tp_traverse tp_traverse reachable->tp_traverse  not in a    cyclic    isolate   reachable->tp_traverse  periodic    cyclic isolate     detection   finalized? marked as finalized? reachable->finalized?  no refs   tp_finalize tp_finalize reachable->tp_finalize  resurrected    (maybe remove    finalized mark)   uncollectable uncollectable (leaked) reachable->uncollectable  cyclic    isolate    (no GC    support)   tp_dealloc tp_dealloc reachable->tp_dealloc  no refs tp_traverse->finalized?  cyclic    isolate   finalized?->tp_finalize  no (mark    as finalized)   tp_clear tp_clear finalized?->tp_clear  yes   tp_finalize->tp_clear  no refs or     cyclic isolate   tp_finalize->tp_dealloc  recommended  call (see  explanation) tp_finalize->tp_dealloc   no refs   tp_clear->uncollectable  cyclic    isolate   tp_clear->tp_dealloc  no refs   tp_free tp_free tp_dealloc->tp_free    direct call  

Erläuterung

  • Wenn ein neues Objekt durch Aufrufen seines Typs konstruiert wird

    1. tp_new wird aufgerufen, um ein neues Objekt zu erstellen.

    2. tp_alloc wird direkt von tp_new aufgerufen, um den Speicher für das neue Objekt zuzuweisen.

    3. tp_init initialisiert das neu erstellte Objekt. tp_init kann bei Bedarf erneut aufgerufen werden, um ein Objekt neu zu initialisieren. Der Aufruf von tp_init kann auch komplett übersprungen werden, zum Beispiel durch den Aufruf von __new__() durch Python-Code.

  • Nach Abschluss von tp_init ist das Objekt einsatzbereit.

  • Einige Zeit nach der Entfernung der letzten Referenz auf ein Objekt

    1. Wenn ein Objekt nicht als finalisiert markiert ist, kann es durch Markierung als finalisiert und durch Aufruf seiner tp_finalize-Funktion finalisiert werden. Python finalisiert ein Objekt *nicht*, wenn die letzte Referenz darauf gelöscht wird; verwenden Sie PyObject_CallFinalizerFromDealloc(), um sicherzustellen, dass tp_finalize immer aufgerufen wird.

    2. Wenn das Objekt als finalisiert markiert ist, kann tp_clear vom Garbage Collector aufgerufen werden, um von dem Objekt gehaltene Referenzen zu löschen. Es wird *nicht* aufgerufen, wenn die Referenzanzahl des Objekts Null erreicht.

    3. tp_dealloc wird aufgerufen, um das Objekt zu zerstören. Um Code-Duplizierung zu vermeiden, ruft tp_dealloc typischerweise tp_clear auf, um die Referenzen des Objekts freizugeben.

    4. Wenn tp_dealloc die Objektzerstörung abschließt, ruft es direkt tp_free auf (normalerweise gesetzt auf PyObject_Free() oder PyObject_GC_Del(), je nach Typ), um den Speicher freizugeben.

  • Die Funktion tp_finalize darf nach Belieben eine Referenz auf das Objekt hinzufügen. Wenn dies geschieht, wird das Objekt wiederbelebt, was seine bevorstehende Zerstörung verhindert. (Nur tp_finalize darf ein Objekt wiederbeleben; tp_clear und tp_dealloc können dies nicht tun, ohne tp_finalize aufzurufen.) Ob das Wiederbeleben eines Objekts die finalisierte Markierung des Objekts entfernt oder nicht, ist nicht garantiert. Derzeit entfernt Python die finalisierte Markierung nicht von einem wiederbelebten Objekt, wenn es Garbage Collection unterstützt (d. h. das Flag Py_TPFLAGS_HAVE_GC ist gesetzt), entfernt die Markierung aber, wenn das Objekt keine Garbage Collection unterstützt; eines oder beide dieser Verhaltensweisen können sich in Zukunft ändern.

  • tp_dealloc kann optional tp_finalize über PyObject_CallFinalizerFromDealloc() aufrufen, wenn es diesen Code zur Unterstützung der Objektzerstörung wiederverwenden möchte. Dies wird empfohlen, da es garantiert, dass tp_finalize vor der Zerstörung immer aufgerufen wird. Siehe die Dokumentation zu tp_dealloc für Beispielcode.

  • Wenn das Objekt Mitglied eines zyklischen Isolats ist und entweder tp_clear den Referenzzyklus nicht aufbrechen kann oder das zyklische Isolat nicht erkannt wird (vielleicht wurde gc.disable() aufgerufen, oder das Flag Py_TPFLAGS_HAVE_GC wurde in einem der beteiligten Typen fälschlicherweise weggelassen), bleiben die Objekte unendlich lange nicht sammelbar (sie "lecken"). Siehe gc.garbage.

Wenn das Objekt als Garbage Collection-unterstützend markiert ist (das Flag Py_TPFLAGS_HAVE_GC ist in tp_flags gesetzt), sind auch die folgenden Ereignisse möglich

  • Der Garbage Collector ruft gelegentlich tp_traverse auf, um zyklische Isolate zu identifizieren.

  • Wenn der Garbage Collector ein zyklisches Isolat entdeckt, finalisiert er eines der Objekte in der Gruppe, indem er es als finalisiert markiert und seine tp_finalize-Funktion aufruft, falls vorhanden. Dies wiederholt sich, bis das zyklische Isolat nicht mehr existiert oder alle Objekte finalisiert wurden.

  • tp_finalize darf das Objekt wiederbeleben, indem es eine Referenz von außerhalb des zyklischen Isolats hinzufügt. Die neue Referenz bewirkt, dass die Gruppe von Objekten nicht mehr als zyklisches Isolat fungiert (der Referenzzyklus kann noch existieren, aber die Objekte sind dann nicht mehr isoliert).

  • Wenn der Garbage Collector ein zyklisches Isolat entdeckt und alle Objekte in der Gruppe bereits als finalisiert markiert wurden, löscht der Garbage Collector eines oder mehrere der nicht gelöschten Objekte in der Gruppe (möglicherweise gleichzeitig), indem er die tp_clear-Funktion jedes Objekts aufruft. Dies wiederholt sich, solange das zyklische Isolat noch existiert und nicht alle Objekte gelöscht wurden.

Zerstörung zyklischer Isolate

Unten sind die Lebensphasen eines hypothetischen zyklischen Isolats aufgeführt, das bestehen bleibt, nachdem jedes Mitgliedsobjekt finalisiert oder gelöscht wurde. Es handelt sich um ein Speicherleck, wenn ein zyklisches Isolat alle diese Phasen durchläuft; es sollte verschwinden, sobald alle Objekte gelöscht sind, wenn nicht früher. Ein zyklisches Isolat kann entweder verschwinden, weil der Referenzzyklus unterbrochen wird, oder weil die Objekte nicht mehr isoliert sind aufgrund von Finalizer-Wiederbelebung (siehe tp_finalize).

  1. Erreichbar (noch kein zyklisches Isolat): Alle Objekte befinden sich in ihrem normalen, erreichbaren Zustand. Ein Referenzzyklus kann existieren, aber eine externe Referenz bedeutet, dass die Objekte noch nicht isoliert sind.

  2. Unerreichbar, aber konsistent: Die letzte Referenz von außerhalb der zyklischen Objektgruppe wurde entfernt, wodurch die Objekte isoliert wurden (somit ist ein zyklisches Isolat geboren). Keines der Objekte der Gruppe wurde bisher finalisiert oder gelöscht. Das zyklische Isolat verbleibt in diesem Zustand bis zu einem zukünftigen Lauf des Garbage Collectors (nicht unbedingt der nächste Lauf, da der nächste Lauf möglicherweise nicht jedes Objekt scannt).

  3. Gemisch aus finalisierten und nicht finalisierten Objekten: Objekte in einem zyklischen Isolat werden einzeln finalisiert, was bedeutet, dass es eine Zeitspanne gibt, in der das zyklische Isolat aus einer Mischung von finalisierten und nicht finalisierten Objekten besteht. Die Reihenfolge der Finalisierung ist nicht spezifiziert, daher kann sie zufällig erscheinen. Ein finalisiertes Objekt muss sich beim Interagieren mit nicht finalisierten Objekten vernünftig verhalten, und ein nicht finalisiertes Objekt muss die Finalisierung einer beliebigen Teilmenge seiner Referenzobjekte tolerieren können.

  4. Alle finalisiert: Alle Objekte in einem zyklischen Isolat werden finalisiert, bevor eines davon gelöscht wird.

  5. Gemisch aus finalisierten und gelöschten Objekten: Die Objekte können seriell oder gleichzeitig (aber mit gehaltener GIL) gelöscht werden, wobei einige vor anderen fertig werden. Ein finalisiertes Objekt muss die Löschung einer Teilmenge seiner Referenzobjekte tolerieren können. PEP 442 bezeichnet diese Phase als "zyklischen Müll".

  6. Geleakt: Wenn ein zyklisches Isolat weiterhin besteht, nachdem alle Objekte in der Gruppe finalisiert und gelöscht wurden, bleiben die Objekte unendlich lange nicht sammelbar (siehe gc.garbage). Es ist ein Fehler, wenn ein zyklisches Isolat diese Phase erreicht – es bedeutet, dass die tp_clear-Methoden der beteiligten Objekte den Referenzzyklus nicht wie erforderlich aufgebrochen haben.

Wenn tp_clear nicht existieren würde, hätte Python keine Möglichkeit, einen Referenzzyklus sicher aufzubrechen. Die bloße Zerstörung eines Objekts in einem zyklischen Isolat würde zu einem hängenden Zeiger führen, was zu undefiniertem Verhalten führt, wenn ein Objekt, das auf das zerstörte Objekt verweist, selbst zerstört wird. Der Löschschritt macht die Objektzerstörung zu einem zweistufigen Prozess: zuerst wird tp_clear aufgerufen, um die Objekte teilweise zu zerstören, um sie voneinander zu trennen, und dann wird tp_dealloc aufgerufen, um die Zerstörung abzuschließen.

Im Gegensatz zum Löschen ist die Finalisierung keine Zerstörungsphase. Ein finalisiertes Objekt muss sich weiterhin ordnungsgemäß verhalten und seine Designverträge erfüllen. Der Finalizer eines Objekts darf beliebigen Python-Code ausführen und darf sogar die bevorstehende Zerstörung verhindern, indem er eine Referenz hinzufügt. Der Finalizer ist nur durch die Aufrufsequenz mit der Zerstörung verbunden – wenn er ausgeführt wird, läuft er vor der Zerstörung, die mit tp_clear (falls aufgerufen) beginnt und mit tp_dealloc endet.

Die Finalisierungsphase ist nicht notwendig, um die Objekte in einem zyklischen Isolat sicher wiederherzustellen, aber ihre Existenz erleichtert das Design von Typen, die sich beim Löschen von Objekten vernünftig verhalten. Das Löschen eines Objekts kann es notwendigerweise in einem fehlerhaften, teilweise zerstörten Zustand hinterlassen – es kann unsicher sein, Methoden des gelöschten Objekts aufzurufen oder auf seine Attribute zuzugreifen. Mit der Finalisierung können nur finalisierte Objekte mit gelöschten Objekten interagieren; nicht finalisierte Objekte interagieren garantiert nur mit nicht gelöschten (aber potenziell finalisierten) Objekten.

Zusammenfassend die möglichen Interaktionen

  • Ein nicht finalisiertes Objekt kann Referenzen auf oder von nicht finalisierten und finalisierten Objekten haben, aber nicht auf oder von gelöschten Objekten.

  • Ein finalisiertes Objekt kann Referenzen auf oder von nicht finalisierten, finalisierten und gelöschten Objekten haben.

  • Ein gelöschtes Objekt kann Referenzen auf oder von finalisierten und gelöschten Objekten haben, aber nicht auf oder von nicht finalisierten Objekten.

Ohne Referenzzyklen kann ein Objekt einfach zerstört werden, sobald seine letzte Referenz gelöscht ist; die Finalisierungs- und Löschschritte sind nicht notwendig, um ungenutzte Objekte sicher wiederzugewinnen. Es kann jedoch nützlich sein, tp_finalize und tp_clear vor der Zerstörung automatisch aufzurufen, da das Typdesign vereinfacht wird, wenn alle Objekte immer dieselbe Ereignisreihe durchlaufen, unabhängig davon, ob sie an einem zyklischen Isolat teilgenommen haben. Python ruft derzeit nur tp_finalize und tp_clear nach Bedarf auf, um ein zyklisches Isolat zu zerstören; dies kann sich in einer zukünftigen Version ändern.

Funktionen

Um Speicher zuzuweisen und freizugeben, siehe Objekte auf dem Heap zuweisen.

void PyObject_CallFinalizer(PyObject *op)

Finalisiert das Objekt wie in tp_finalize beschrieben. Rufen Sie diese Funktion (oder PyObject_CallFinalizerFromDealloc()) anstelle des direkten Aufrufs von tp_finalize auf, da diese Funktion mehrere Aufrufe von tp_finalize deduplizieren kann. Derzeit werden Aufrufe nur dedupliziert, wenn der Typ Garbage Collection unterstützt (d. h. das Flag Py_TPFLAGS_HAVE_GC gesetzt ist); dies kann sich in Zukunft ändern.

int PyObject_CallFinalizerFromDealloc(PyObject *op)

Dasselbe wie PyObject_CallFinalizer(), aber dafür bestimmt, am Anfang des Objekt-Destruktors (tp_dealloc) aufgerufen zu werden. Es dürfen keine Referenzen auf das Objekt vorhanden sein. Wenn der Finalizer des Objekts das Objekt wiederbelebt, gibt diese Funktion -1 zurück; es sollte keine weitere Zerstörung stattfinden. Andernfalls gibt diese Funktion 0 zurück und die Zerstörung kann normal fortgesetzt werden.

Siehe auch

tp_dealloc für Beispielcode.