Start · Sprachen · PHP · Referenz · gc_collect_cycles

gc_collect_cycles

Funktion

Erzwingt die sofortige Ausführung des Garbage Collectors, um zirkuläre Referenzen aufzulösen, und gibt die Anzahl der gesammelten Zyklen zurück.

seit PHP 5.3.0 Kategorie: misc

Signatur

gc_collect_cycles(): int

Beschreibung

gc_collect_cycles() löst manuell einen Durchlauf des zyklischen Garbage Collectors aus. Normalerweise wird der Garbage Collector von PHP automatisch gestartet, wenn der interne Puffer für verdächtige Wurzel-Referenzen voll läuft (standardmäßig nach 10.000 Einträgen). Mit dieser Funktion lässt sich dieser Vorgang gezielt zu einem selbst gewählten Zeitpunkt anstoßen.

Zirkuläre Referenzen entstehen, wenn Objekte oder Arrays wechselseitig aufeinander verweisen (z. B. Eltern–Kind-Beziehungen), sodass der normale Referenzzähler niemals auf null fällt. Ohne den zyklischen Garbage Collector würde der belegte Speicher nie freigegeben und zu einem Speicherleck führen.

Die Funktion ist besonders nützlich in lang laufenden Skripten, Daemon-Prozessen oder CLI-Applikationen, bei denen in Schleifen viele Objekte mit gegenseitigen Referenzen erzeugt und verworfen werden. Durch den gezielten Aufruf kann man Speicherspitzen kontrollieren, ohne auf den automatischen Auslöser warten zu müssen.

Voraussetzung ist, dass die Garbage Collection aktiv ist (gc_enable() bzw. zend.enable_gc = On in der php.ini). Ist sie deaktiviert, führt gc_collect_cycles() zwar einen Lauf durch, sammelt aber praktisch nichts ein.

Rückgabewert

Typ
int
Beschreibung
Gibt die Anzahl der eingesammelten Referenzzyklen (Objektgraphen) zurück. Ein Wert von 0 bedeutet, dass keine zirkulären Referenzen gefunden wurden.

Beispiele

Zirkuläre Referenzen manuell einsammeln und Speicher prüfen

<?php
class Node {
    public ?Node $child = null;
    public ?Node $parent = null;
}

// Zirkuläre Referenz erzeugen
$a = new Node();
$b = new Node();
$a->child  = $b;
$b->parent = $a;

// Referenzen aufheben, aber Zyklus bleibt im Speicher
unset($a, $b);

$vorher = memory_get_usage();
$gesammelt = gc_collect_cycles();
$nachher = memory_get_usage();

echo "Gesammelte Zyklen: {$gesammelt}\n";
echo "Speicher vorher: {$vorher} Bytes\n";
echo "Speicher nachher: {$nachher} Bytes\n";
echo "Freigegeben: " . ($vorher - $nachher) . " Bytes\n";
Gesammelte Zyklen: 1 Speicher vorher: 374512 Bytes Speicher nachher: 374208 Bytes Freigegeben: 304 Bytes

Regelmäßige GC-Aufrufe in einer langen Verarbeitungsschleife

<?php
gc_enable(); // Sicherstellen, dass GC aktiv ist

class Job {
    public ?Job $next = null;
    public array $data = [];
}

$gesamtGesammelt = 0;

for ($i = 0; $i < 10000; $i++) {
    $job1 = new Job();
    $job2 = new Job();
    $job1->next = $job2; // zirkuläre Referenz
    $job2->next = $job1;
    $job1->data = range(1, 100);

    unset($job1, $job2);

    // Alle 500 Iterationen GC manuell anstoßen
    if ($i % 500 === 499) {
        $gesamtGesammelt += gc_collect_cycles();
    }
}

echo "Insgesamt gesammelte Zyklen: {$gesamtGesammelt}\n";
Insgesamt gesammelte Zyklen: 10000

// Wichtig · Fallstricke

Performance: Ein GC-Lauf ist nicht kostenlos. In sehr engen, zeitkritischen Schleifen sollte man die Aufrufhäufigkeit abwägen. Zu häufige Aufrufe können die Laufzeit messbar erhöhen. Als Faustregel empfiehlt es sich, gc_collect_cycles() nur periodisch (z. B. alle N Iterationen) aufzurufen.

Zusammenspiel mit gc_disable(): Wenn der automatische GC per gc_disable() oder zend.enable_gc = Off abgeschaltet wurde, führt gc_collect_cycles() dennoch einen einmaligen Lauf durch. Das ist in manchen Szenarien nützlich, um den GC zu einem selbst kontrollierten Zeitpunkt gezielt zu aktivieren, ohne die automatische Auslösung zu erlauben.

Circular-Reference-Erkennung: PHP erkennt nur Zyklen in Objekt- und Array-Strukturen. Ressourcen und skalare Werte werden nicht berücksichtigt.