0Pricing
PHP Academy · Lektion

Speicherverwaltung und Garbage Collection

Verstehen, wie zvals, Refcounting und der GC funktionieren

Speicherverwaltung und Garbage Collection ist eine kostenlose PHP Academy-Lektion auf CoddyKit. Dies ist Lektion 2 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des PHP Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der PHP Academy-Kurs umfasst insgesamt 4 Lektionen.

Wie PHP den Speicher verwaltet

PHP-Werte werden in zvals gespeichert und größtenteils über Referenzzählung verwaltet. Ein Zyklus-Garbage-Collector erkennt, was die Referenzzählung nicht erfassen kann. Das Verständnis dieser Mechanismen erklärt Speicherlecks in lang laufenden Workern, die Kosten von Kopien und wann gc_collect_cycles() relevant ist.

Das zval

Ein zval ist der Container für jeden PHP-Wert: ein Typ-Tag plus eine Union zur Speicherung des Werts. Skalare (int, float, bool) werden als Wert im zval gespeichert und NICHT per Referenzzählung verwaltet. Komplexe Typen (string, array, object) verweisen auf eine referenzgezählte Struktur im Heap.

<?php
// debug_zval_refcount-style inspection via gc + count
$arr = [1, 2, 3];
$copy = $arr;   // refcount of the array bumps, no data copy yet (CoW)

// Modifying triggers copy-on-write separation
$copy[] = 4;

var_dump($arr);   // still [1,2,3]
var_dump($copy);  // [1,2,3,4]
?>

Referenzzählung

Jeder referenzgezählte Wert erfasst, auf wie viele zvals er verweist. Eine Zuweisung erhöht den Zähler; beim Aufheben einer Zuordnung oder beim Verlassen des Gültigkeitsbereichs wird er verringert. Erreicht der Zähler null, wird der Speicher sofort freigegeben – ohne auf einen GC-Durchlauf zu warten. Der Großteil des PHP-Speichers wird auf diese Weise zurückgewonnen.

<?php
$a = str_repeat('x', 1000);  // refcount 1 on the string
$b = $a;                      // refcount 2
unset($a);                    // refcount 1
unset($b);                    // refcount 0 -> freed instantly

echo "Refcounting frees deterministically\n";
?>

Copy-on-Write

Bei einer Zuweisung werden Daten nicht kopiert; sie werden geteilt und der Referenzzähler erhöht (Copy-on-Write). Die tatsächliche Duplizierung erfolgt erst beim ersten Schreibzugriff auf einen gemeinsam genutzten Wert. Dadurch ist das Übergeben großer Arrays günstig, solange sie nicht geändert werden.

<?php
$big = range(1, 100000);
$shared = $big;          // O(1): shared, refcount++

// Reads stay shared and cheap
echo count($shared), PHP_EOL;

// First write separates (copies) — now it costs O(n)
$shared[0] = -1;
echo $big[0], ' vs ', $shared[0], PHP_EOL;  // 1 vs -1
?>

Das Problem: Zyklen

Die Referenzzählung allein kann Referenzzyklen nicht freigeben. Wenn Objekt A eine Referenz auf B und B wiederum eine auf A hält, erreichen ihre Zähler nie null – selbst nachdem Sie jede externe Variable freigegeben haben. Der Speicher bleibt belegt, bis der Zyklus-Garbage-Collector ausgeführt wird.

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

$a = new Node();
$b = new Node();
$a->ref = $b;   // a -> b
$b->ref = $a;   // b -> a  (cycle!)

unset($a, $b);  // external refs gone, but internal cycle keeps refcount > 0

// The objects are unreachable but NOT yet freed by refcounting alone.
echo gc_collect_cycles() . " cycles collected\n";
?>

Der Zyklus-Garbage-Collector

PHPs GC implementiert ein synchrones Mark-and-Sweep über Wurzeln. Potenzielle Zyklus-Wurzeln (Werte, deren Referenzzähler verringert wurde, aber nicht null erreicht hat) werden gepuffert. Wenn der Wurzelpuffer voll ist (standardmäßig 10.000), läuft der GC: Er simuliert Dekrementierungen, ermittelt tatsächlich unerreichbare Gruppen und gibt sie frei.

<?php
// GC status exposes the root buffer and run stats
for ($i = 0; $i < 3; $i++) {
    $a = new stdClass();
    $b = new stdClass();
    $a->b = $b; $b->a = $a;   // create a cycle
    unset($a, $b);            // becomes a GC root
}

print_r(gc_status());
?>

GC steuern

Sie können den Collector steuern: gc_disable()/gc_enable() schalten ihn aus beziehungsweise ein, und gc_collect_cycles() erzwingt einen Durchlauf und gibt die Anzahl der freigegebenen Zyklen zurück. Bei durchsatzkritischen Batch-Jobs können Sie den GC während einer engen Phase deaktivieren und anschließend am Ende einmal ausführen.

<?php
gc_disable();              // no automatic cycle collection

// ... heavy phase that creates and drops many cycles ...
for ($i = 0; $i < 1000; $i++) {
    $x = new stdClass(); $y = new stdClass();
    $x->y = $y; $y->x = $x; unset($x, $y);
}

$freed = gc_collect_cycles();   // reclaim all at once
gc_enable();
echo "Freed $freed objects\n";
?>

Lang laufende Worker

In CLI-Daemons, Queue-Consumern sowie Swoole- und RoadRunner-Workern wird der Speicher nicht durch das Beenden von Anfragen freigegeben – der Prozess läuft stundenlang. Achten Sie auf statische Caches, die unbeschränkt wachsen, sich ansammelnde Zyklen und nicht geschlossene Ressourcen. Überwachen Sie die Nutzung mit memory_get_usage(true) und starten Sie Worker regelmäßig neu.

<?php
$peakStart = memory_get_usage(true);

// Simulate processing a batch of jobs
for ($job = 0; $job < 5; $job++) {
    $payload = range(0, 10000);
    // ... process ...
    unset($payload);
}

printf("start: %d KB, peak: %d KB, now: %d KB\n",
    $peakStart >> 10,
    memory_get_peak_usage(true) >> 10,
    memory_get_usage(true) >> 10
);
?>

WeakReference & WeakMap

Wenn Sie Daten mit einem Objekt cachen oder verknüpfen möchten, ohne es am Leben zu halten, verwenden Sie WeakReference (8.0) und WeakMap (8.0). Ein WeakMap-Eintrag erhöht den Referenzzähler des Schlüsselobjekts nicht. Dadurch kann das Objekt eingesammelt und sein Eintrag automatisch entfernt werden – ideal für Metadaten-Nebentabellen, die keine Leaks verursachen dürfen.

<?php
$map = new WeakMap();

$obj = new stdClass();
$map[$obj] = 'metadata';      // does NOT keep $obj alive

echo count($map) . PHP_EOL;   // 1
unset($obj);                  // object collectible -> entry removed
echo count($map) . PHP_EOL;   // 0
?>

Internierte Strings und unveränderliche Werte

String-Literale in Ihrem Quelltext sind interniert: Sie werden einmal gespeichert, geteilt und während der Anfrage weder über die Referenzzählung verwaltet noch freigegeben. OPcache verschiebt internierte Strings und unveränderliche (gecachete) op_arrays in den gemeinsamen Speicher, sodass sie über Anfragen hinweg geteilt werden. Deshalb sind String-Literale beim Kopieren praktisch kostenlos.

Speicherlimit und Allocator

PHP verwendet seinen eigenen emalloc-Allocator (Zend Memory Manager), der auf dem System-Allocator aufsetzt und die Nutzung pro Anfrage gegenüber memory_limit erfasst. Wird das Limit erreicht, tritt ein fataler Fehler auf. true in memory_get_usage(true) meldet tatsächlich vom Betriebssystem zugewiesene Blöcke, nicht nur das, was Ihr Skript derzeit hält.

<?php
echo 'limit: ' . ini_get('memory_limit') . PHP_EOL;
echo 'emalloc usage: ' . memory_get_usage() . " bytes\n";   // ZendMM tracked
echo 'real usage:    ' . memory_get_usage(true) . " bytes\n"; // from OS
?>

Kurztest

Warum kann die Referenzzählung allein bei einigen Objekten zu Speicherlecks führen?

Zusammenfassung

PHP speichert Werte in zvals. Skalare werden als Wert gespeichert, komplexe Typen auf dem Heap alloziert und per Referenzzählung mit Copy-on-Write verwaltet. Die Referenzzählung gibt Speicher bei Zählerstand null deterministisch frei, kann aber Zyklen nicht zurückgewinnen. Deshalb läuft ein synchroner Mark-and-Sweep-Collector, sobald der Wurzelpuffer voll ist, oder über gc_collect_cycles(). Verwenden Sie WeakMap/WeakReference für nicht besitzende Verknüpfungen und achten Sie bei lang laufenden Workern auf unbeschränktes Wachstum.

Häufig gestellte Fragen

Ist die Lektion „Speicherverwaltung und Garbage Collection“ kostenlos?

Ja — der vollständige Text von „Speicherverwaltung und Garbage Collection“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des PHP Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der PHP Academy-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „Speicherverwaltung und Garbage Collection“?

Verstehen, wie zvals, Refcounting und der GC funktionieren Du übst PHP Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um PHP Academy zu starten?

Keine Vorkenntnisse erforderlich. PHP Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 2 von 4.

Wie lange dauert die Lektion „Speicherverwaltung und Garbage Collection“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser PHP Academy-Lektion Code schreiben und ausführen?

Ja. Jede PHP Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. So funktioniert die Zend Engine
  2. Speicherverwaltung und Garbage Collection
  3. OPcache und JIT-Kompilierung
  4. Eine einfache PHP-Erweiterung in C schreiben
← Zurück zu PHP Academy