Sammeln zyklischer Referenzen
   
    In der Vergangenheit konnten referenzzählende Speichermechanismen, wie der
    von PHP verwendete, zyklische Referenzspeicherlecks nicht beheben. Seit
    Version 5.3.0 implementiert PHP jedoch den synchronen Algorithmus aus der
    Abhandlung
    » Concurrent Cycle Collection in Reference Counted Systems,
    die dieses Problem behandelt.
   
   
    Eine vollständige Erklärung der Funktionsweise des Algorithmus würde den
    Rahmen dieses Abschnitts etwas sprengen, aber die Grundlagen werden hier
    erläutert. Zunächst müssen ein paar Grundregeln festgelegt werden. Wenn
    ein Referenzzähler erhöht wird, ist er noch in Gebrauch und daher kein
    Garbage. Wenn der Referenzzähler verringert wird und Null erreicht, kann
    das Zval freigegeben werden. Das bedeutet, dass problematische Zyklen nur
    dann erzeugt werden können, wenn ein refcount-Argument auf einen Wert
    ungleich Null verringert wird. Damit kann festgestellt werden, welche
    Teile in einem problematischen Zyklus Garbage sind, indem geprüft wird, ob
    es möglich ist, ihren Referenzzähler um eins zu verringern, und dann
    geprüft wird, welche der Zvals einen Referenzzähler von Null haben.
   
   
     
   
   
    Um zu vermeiden, dass die Überprüfung von problematischen Zyklen bei jeder
    möglichen Verringerung eines Referenzzählers aufgerufen werden muss, legt
    der Algorithmus stattdessen alle möglichen Wurzeln (Zvals) in den
    "Wurzelpuffer" (und markiert sie "lila"). Er sorgt auch dafür, dass jede
    mögliche Wurzel nur einmal im Puffer landet. Erst wenn der Wurzelpuffer
    voll ist, beginnt der Sammelmechanismus für alle verschiedenen Zvals
    darin. Siehe Schritt A in der Abbildung oben.
   
   
    In Schritt B führt der Algorithmus bei allen möglichen Wurzeln eine
    Tiefensuche durch, um die Referenzzähler jedes gefundenen Zvals um eins zu
    verringern. Dabei wird darauf geachtet, dass der Referenzzähler desselben
    Zvals nicht zweimal verringert wird (indem sie als "grau" markiert
    werden). In Schritt C führt der Algorithmus erneut eine Tiefensuche bei
    jedem Wurzelknoten aus, um den Referenzzähler jedes Zvals erneut zu
    überprüfen. Stellt er fest, dass der Referenzzähler Null ist, wird das
    Zval als "weiß" markiert (in der Abbildung blau). Ist er größer als Null,
    wird die Verringerung des Refcount um eins mit einer Tiefensuche von
    diesem Punkt an rückgängig gemacht, und die Zvals werden wieder "schwarz"
    markiert. Im letzten Schritt (D) geht der Algorithmus über den
    Wurzelpuffer und entfernt die Zval-Wurzeln von dort, wobei er gleichzeitig
    überprüft, welche Zvals im vorherigen Schritt als "weiß" markiert wurden.
    Jedes Zval, das als "weiß" markiert wurde, wird freigegeben.
   
   
    Nachdem nun ein grundlegendes Verständnis für die Funktionsweise des
    Algorithmus vorhanden ist, wird nun erläutert, wie dieser in PHP
    integriert ist. Standardmäßig ist der Garbage Collector von PHP aktiviert,
    aber es gibt eine php.ini-Einstellung, mit der dies geändert werden
    kann: zend.enable_gc.
   
   
    Wenn der Garbage Collector aktiviert ist, wird der oben beschriebene
    Algorithmus zum Finden von Zyklen immer dann ausgeführt, wenn der
    Wurzelpuffer voll ist. Der Wurzelpuffer hat eine feste Größe von 10.000
    möglichen Wurzeln (obwohl man dies ändern kann, indem man im PHP-Quellcode
    in Zend/zend_gc.c die Konstante
    GC_THRESHOLD_DEFAULT ändert und PHP neu kompiliert).
    Wenn der Garbage Collector deaktiviert ist, wird der Algorithmus zum
    Finden von Zyklen nie ausgeführt. Allerdings werden mögliche Wurzeln immer
    im Wurzelpuffer aufgezeichnet, unabhängig davon, ob der Mechanismus der
    Garbage Collection mit dieser Konfigurationseinstellung aktiviert wurde.
   
   
    Wenn der Wurzelpuffer bei deaktiviertem Mechanismus der Garbage Collection
    vollständig mit möglichen Wurzeln gefüllt ist, werden weitere mögliche
    Wurzeln einfach nicht aufgezeichnet. Die möglichen Wurzeln, die nicht
    aufgezeichnet werden, werden vom Algorithmus nie analysiert. Wären sie
    Teil eines zirkulären Referenzzyklus, würden sie nie bereinigt werden und
    ein Speicherleck verursachen.
   
   
    Dass mögliche Wurzeln aufgezeichnet werden, auch wenn der Mechanismus
    deaktiviert wurde, liegt daran, dass es schneller ist, mögliche Wurzeln
    aufzuzeichnen, als jedes Mal, wenn eine mögliche Wurzel gefunden wurde, zu
    prüfen, ob der Mechanismus aktiviert ist. Der Mechanismus der Garbage
    Collection und Analyse selbst kann jedoch sehr viel Zeit in Anspruch
    nehmen.
   
   
    Neben der Änderung der Konfigurationseinstellung
    zend.enable_gc ist es auch
    möglich, den Mechanismus der Garbage Collection zu aktivieren oder zu
    deaktivieren, indem man gc_enable() bzw.
    gc_disable() aufruft. Diese Funktionen haben den
    gleichen Effekt wie die Konfigurationseinstellung, mit der der Mechanismus
    aktiviert oder deaktiviert wird. Es ist auch möglich, das Sammeln von
    Zyklen zu erzwingen, selbst wenn der Wurzelpuffer noch nicht voll ist.
    Dazu kann die Funktion gc_collect_cycles() verwendet
    werden. Diese Funktion ermittelt, wieviele Zyklen durch den Algorithmus
    gesammelt wurden.
   
   
    Der Grund für die Möglichkeit, den Mechanismus zu aktivieren und zu
    deaktivieren und die Sammlung von Zyklen selbst einzuleiten, besteht darin,
    dass einige Teile einer Anwendung sehr zeitkritisch sein könnten. In
    diesen Fällen ist es vielleicht nicht erwünscht, dass der Mechanismus der
    Garbage Collection ausgelöst wird. Wenn die Garbage Collection für
    bestimmte Teile einer Anwendung ausgeschaltet wird, besteht natürlich die
    Gefahr, dass Speicherlecks entstehen, weil einige mögliche Wurzeln nicht
    in den begrenzten Wurzelpuffer passen. Daher ist es wahrscheinlich ratsam,
    gc_collect_cycles() kurz vor dem Aufruf von
    gc_disable() aufzurufen, um den Speicher freizugeben,
    der durch eventuell bereits im Wurzelpuffer aufgezeichnete Wurzeln
    verloren gegangen sein könnte. Dadurch bleibt ein leerer Puffer übrig,
    sodass mehr Platz für die Speicherung möglicher Wurzeln vorhanden ist,
    während der Mechanismus zum Sammeln von Zyklen deaktiviert ist.