Hukommelseshåndtering og garbage collection
Se, hvordan zvals, refcounting og GC fungerer.
Hukommelseshåndtering og garbage collection er en gratis PHP Academy-lektion på CoddyKit. Dette er lektion 2 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i PHP Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. PHP Academy-kurset indeholder 4 lektioner i alt.
Sådan håndterer PHP hukommelse
PHP-værdier ligger i zvals og håndteres hovedsageligt med referencetælling samt en cyklusopsamler, der finder det, referencetælling ikke kan. Når du forstår dette, bliver det lettere at forstå hukommelseslækager i langvarige arbejdere, omkostningen ved kopier, og hvornår gc_collect_cycles() er relevant.
zval'en
En zval er beholderen for enhver PHP-værdi: et typemærke samt en union, der indeholder værdien. Skalarer (int, float, bool) gemmes efter værdi i zval'en og tælles IKKE med i referencetællingen. Komplekse typer (string, array, object) peger på en struktur med referencetælling på heapen.
<?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]
?>Referencetælling
Hver værdi med referencetælling registrerer, hvor mange zvals der peger på den. En tildeling øger tælleren; ophævelse af en variabel eller det at forlade et omfang mindsker den. Når tælleren når nul, frigives hukommelsen straks — der ventes ikke på en GC-kørsel. Det meste hukommelse i PHP frigives på denne måde.
<?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";
?>Kopiering ved skrivning
En tildeling kopierer ikke dataene; de deles, og referencetælleren øges (copy-on-write). Den faktiske kopiering sker først ved den første skrivning til en delt værdi. Derfor er det billigt at sende store arrays som argumenter, indtil de ændres.
<?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
?>Problemet: Cyklusser
Referencetælling alene kan ikke frigive referencecyklusser. Hvis objekt A har en reference til B, og B har en tilbage til A, når deres tællere aldrig nul, selv efter at du ophæver alle eksterne variabler — hukommelsen lækker, indtil cyklusopsamleren kører.
<?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";
?>Cyklusopsamleren
PHP's GC implementerer synkron mark-and-sweep over rødder. Potentielle cyklus-rødder (værdier, hvis referencetæller blev reduceret uden at nå nul) gemmes i en buffer. Når rod-bufferen er fuld (som standard 10.000), kører GC: Den simulerer reduktioner, finder grupper, der reelt ikke kan nås, og frigiver dem.
<?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());
?>Styring af GC
Du kan styre opsamleren: gc_disable()/gc_enable() slår den fra og til, mens gc_collect_cycles() tvinger en kørsel igennem og returnerer antallet af frigivne værdier. I batchjob, hvor gennemløbet er kritisk, slår man nogle gange GC fra under en intensiv fase og kører den én gang til sidst.
<?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";
?>Langvarige arbejdere
I CLI-dæmoner, køforbrugere og Swoole/RoadRunner-arbejdere frigives hukommelsen ikke, når anmodningen afsluttes — processen kører i timevis. Hold øje med statiske cacher, der vokser uden grænser, ophobede cyklusser og ressourcer, der ikke er lukket. Overvåg med memory_get_usage(true), og genstart arbejdere med jævne mellemrum.
<?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 og WeakMap
Hvis du vil cache eller knytte data til et objekt uden at holde det i live, kan du bruge WeakReference (8.0) og WeakMap (8.0). En post i WeakMap øger ikke nøgleobjektets referencetæller, så objektet kan opsamles, og posten fjernes automatisk — ideelt til sidetabeller med metadata, der ikke må lække.
<?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
?>Internede strenge og uforanderlige værdier
Strengliteraler i din kildekode er internede: De gemmes én gang, deles og får hverken referencetælling eller frigives under anmodningen. OPcache flytter internede strenge og uforanderlige (cachede) op_arrays til delt hukommelse på tværs af anmodninger. Derfor er det reelt gratis at kopiere strengliteraler.
Hukommelsesgrænse og allokator
PHP bruger sin egen emalloc-allokator (Zend Memory Manager), der ligger oven på systemets allokator og holder styr på forbruget pr. anmodning i forhold til memory_limit. Hvis grænsen nås, udløses en fatal fejl. true i memory_get_usage(true) rapporterer reelt allokerede blokke fra operativsystemet, ikke kun det, dit script aktuelt indeholder.
<?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
?>Hurtigt tjek
Hvorfor kan referencetælling alene føre til hukommelseslækager for nogle objekter?
Opsummering
PHP gemmer værdier i zvals; skalarer gemmes efter værdi, mens komplekse typer allokeres på heapen og håndteres med referencetælling og copy-on-write. Referencetælling frigiver deterministisk ved tælleren nul, men kan ikke genvinde cyklusser, så en synkron mark-and-sweep-opsamler kører, når rod-bufferen er fuld (eller via gc_collect_cycles()). Brug WeakMap/WeakReference til associationer, der ikke ejer objekterne, og hold øje med ubegrænset vækst i langvarige arbejdere.
Lær PHP med en AI-underviser — gratis
Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.
- Kurser
- 49
- Lektioner
- 195
Ofte stillede spørgsmål
Er lektionen “Hukommelseshåndtering og garbage collection” gratis?
Ja — hele teksten til “Hukommelseshåndtering og garbage collection” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af PHP Academy-kurset, skal du opgradere til CoddyKit PRO. PHP Academy-kurset indeholder 4 lektioner i alt.
Hvad lærer jeg i “Hukommelseshåndtering og garbage collection”?
Se, hvordan zvals, refcounting og GC fungerer. Du øver dig i PHP Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.
Skal jeg have erfaring for at begynde på PHP Academy?
Der kræves ingen tidligere erfaring. PHP Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 2 af 4.
Hvor lang tid tager lektionen “Hukommelseshåndtering og garbage collection”?
De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.
Kan jeg skrive og køre kode i denne PHP Academy-lektion?
Ja. Alle PHP Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.
Alle lektioner i dette kursus
- Sådan fungerer Zend Engine
- Hukommelseshåndtering og garbage collection
- OPcache og JIT-kompilering
- Skriv en basal PHP-udvidelse i C