Kort sagt: PHP frigiver hukommelse først og fremmest via referencetælling: Hver værdi ved, hvor mange variabler der peger på den, og slettes, så snart tælleren når nul. Til krydsreferencer, som på den måde aldrig ville blive frigivet, findes en cyklus-garbage-collector. Ved afslutningen af hver forespørgsel rydder PHP desuden op i alt, hvad forespørgslen har optaget. Begge PHP2Shell-sårbarheder sætter ind ved hukommelsestilstande: ved en out-of-memory-tilstand under behandlingen af forespørgslen og ved intern tilstand, der bliver bestående for længe efter frigivelsen.
Sådan administrerer PHP hukommelse: overblikket
I sprog som C skal programmøren selv anmode om og frigive hukommelse. PHP tager det over. Hver variabel, hvert array og hvert objekt optager hukommelse, som PHP administrerer via sin egen hukommelsesmanager. Grænsen herfor sætter memory_limit, ifølge manualen som standard 128M.
For beslutningstagere er ét punkt centralt: Ved klassisk drift med PHP-FPM eller mod_php kører langlivede worker-processer, der behandler mange forespørgsler efter hinanden. Hvad en forespørgsel efterlader, skal være ryddet pålideligt op, før den næste kommer. Netop her ligger en af PHP2Shell-sårbarhederne.
Referencetælling: tæl, hvem der bruger en værdi
PHP gemmer hver værdi i en container, den såkaldte zval. Ved siden af type og indhold fører den ifølge manualen en referencetæller (refcount) og en markering af, om værdien er en del af en PHP-reference med & (is_ref).
<?php
$a = str_repeat('x', 1000); // én værdi, refcount 1
$b = $a; // ingen kopiering, refcount 2
$b .= 'y'; // først nu kopieres der (copy-on-write)
unset($a); // originalens refcount falder
// Når en tæller når 0, frigiver PHP hukommelsen straks.
Princippet hedder copy-on-write: PHP kopierer først en værdi, når en af variablerne ændrer den. Det sparer meget hukommelse. En variabels tæller kan gøres synlig med udvidelsen Xdebug og funktionen xdebug_debug_zval(). Manualen gør opmærksom på, at de nøjagtige tal kan afvige fra PHP-version til PHP-version.
Cyklusser: når referencetælling ikke slår til
Referencetælling har en kendt svaghed. Peger en struktur på sig selv, falder dens tæller aldrig til nul.
<?php
$a = ['eins'];
$a[] = &$a; // arrayet indeholder en reference til sig selv
unset($a); // ingen kan længere tilgå det, men refcount forbliver 1
Ifølge manualen forekommer sådanne krydsreferencer især ved objekter, f.eks. i forælder-barn-strukturer eller parsere. Enkelte lækager er harmløse, fordi PHP rydder op ved afslutningen af forespørgslen. I langvarigt kørende processer som kø-workers eller store testkørsler kan millioner af dem dog summe sig til et reelt hukommelsesproblem.
Cyklus-garbage-collectoren
Siden PHP 5.3.0 bruger PHP en algoritme efter Bacon og Rajan til at finde sådanne cyklusser. Grundtanken: En cyklus kan kun opstå, hvis en tæller falder til en værdi større end nul. Kun disse værdier husker PHP som mulige rødder.
- Mulige rødder havner i en buffer. Manualen nævner 10.000 poster som standardstørrelse; den aktuelt gældende tærskelværdi viser
gc_status()i feltetthreshold. - Er bufferen fuld, gennemløber PHP alle tilgængelige værdier og nedsætter på prøve deres tællere.
- I en anden omgang gælder: Værdier med tæller nul er affald, ved alle andre fortrydes nedsættelsen.
- Rødderne fjernes fra bufferen, og alle værdier, der er genkendt som affald, frigives.
Det styres via zend.enable_gc (som standard slået til) eller under kørslen med gc_enable() og gc_disable(). gc_collect_cycles() fremtvinger en kørsel og returnerer antallet af indsamlede cyklusser.
Mål og observér hukommelse
| Funktion | Formål |
|---|---|
memory_get_usage() |
hukommelse, der aktuelt er optaget af scriptet |
memory_get_usage(true) |
hukommelse, som hukommelsesmanageren har reserveret hos systemet; mod denne værdi kontrolleres memory_limit |
memory_get_peak_usage() |
højeste niveau af optaget hukommelse |
gc_status() |
kørsler, indsamlede cyklusser, tærskelværdi, rødder; siden PHP 8.3.0 også køretider |
gc_collect_cycles() |
udløs cyklusindsamling straks |
WeakMap |
tilknytning til objekter uden at forhindre deres frigivelse, siden PHP 8.0 |
Ifølge manualen registrerer memory_get_usage() kun hukommelse, som PHP anmoder om via sin egen hukommelsesmanager. Hvad udvidelser henter direkte hos operativsystemet, mangler i disse tal. Til hele processens hukommelsesgrænse er det derfor operativsystemet eller containeren, der er ansvarlig.
Hukommelsesstyring og PHP2Shell
Beskrivelserne af begge sårbarheder fører direkte ind i dette emne. Ved CVE-2026-109618 kan en out-of-memory-tilstand afbryde PHP, mens forespørgselsdata kun er delvist initialiseret; ved oprydningen kan ugyldige data så behandles som gyldige. Hvad der sker internt ved et hukommelsesoverløb, forklarer siden memory_limit og out-of-memory. Ved CVE-2026-109619 kan en forespørgsels interne tilstand bestå efter frigivelsen af hukommelsen og bruges af en senere forespørgsel i den samme worker, før den nulstilles. Forløbet fra start til oprydning beskriver siden Request-livscyklussen.
Som midlertidig foranstaltning anbefaler php.net at sætte memory_limit=-1, så applikationer ikke kan overskrive det, f.eks. med php_admin_value i FPM-poolen, og i stedet begrænse hukommelsen via container eller cgroups, ikke via RLIMIT_AS eller RLIMIT_DATA. Hvordan det gøres med systemd, viser indlægget Sæt hukommelsesgrænser med systemd og cgroups.
; FPM-pool: midlertidig foranstaltning ifølge php.net, genstart derefter alle workers
php_admin_value[memory_limit] = -1
Uden en grænse via container eller cgroup kan en enkelt proces udnytte serveren fuldt ud. Og ingen indstilling erstatter opdateringen til PHP 8.5.12, 8.4.27, 8.3.36 eller 8.2.35 inklusive genstart af alle workers, for kun genstarten fjerner gamle processer fra hukommelsen.
Hvordan hjælper PHP SOS konkret?
- Vi kontrollerer de gældende hukommelsesindstillinger pr. pool og om applikationer kan overskrive dem.
- Vi gennemfører de midlertidige foranstaltninger med en hukommelsesgrænse på niveau med container eller cgroup og tester belastningen.
- Vi indspiller opdateringen, genstarter alle workers og dokumenterer, at der ikke længere kører gamle processer, som led i vores PHP-patchservice.
- Vi gennemgår hukommelsesfejl og afbrudte workers i logs fra 20.10.2026.
Ofte stillede spørgsmål
Hvad er referencetælling i PHP?
PHP tæller for hver værdi, hvor mange variabler eller elementer der henviser til den. Falder tælleren til nul, frigives hukommelsen straks. Sådan gives det meste af hukommelsen tilbage uden en egen oprydningskørsel.
Hvorfor har PHP så stadig brug for en garbage collector?
Til cyklusser. Henviser et array til sig selv, eller kender to objekter hinanden gensidigt, falder tælleren aldrig til nul, selv om ingen længere tilgår dem. Cyklus-garbage-collectoren finder og fjerner sådanne strukturer.
Frigiver PHP hukommelsen ved afslutningen af en forespørgsel?
Ja, hukommelse, som et script optager under en forespørgsel, rydder PHP op ved forespørgslens afslutning. Ved PHP2Shell angår CVE-2026-109619 dog netop det tilfælde, at intern tilstand kan bestå efter frigivelsen og bruges af en senere forespørgsel i den samme worker.
Hvorfor anbefaler php.net memory_limit=-1 som midlertidig foranstaltning?
Sårbarheden CVE-2026-109618 hænger sammen med en out-of-memory-tilstand. php.net anbefaler at sætte memory_limit=-1, så applikationer ikke kan overskrive det, og i stedet begrænse hukommelsen via container eller cgroups. Det er ikke en fuldstændig beskyttelse og erstatter ikke opdateringen.