Aktuellt läge: PHP2Shell. Två kritiska PHP-sårbarheter (CVSS 9,2) tillåter skadlig kod utan inloggning. Patchar sedan den 21 oktober 2026.Vad som bör göras nu →

Minneshantering i PHP: referensräkning och garbage collection

PHP hanterar minne med referensräkning och en garbage collector för cykler. Sidan förklarar mekaniken, de viktigaste mätfunktionerna och varför båda PHP2Shell-luckorna har med minnestillstånd att göra.

Aktualitet 5 min läsningPHP-kunskap

Kort sagt: PHP frigör minne framför allt genom referensräkning: Varje värde vet hur många variabler som pekar på det och raderas så snart räknaren når noll. För cirkelreferenser, som på så sätt aldrig skulle frigöras, finns en cykel-garbage-collector. I slutet av varje förfrågan städar PHP dessutom bort allt som förfrågan har tagit i anspråk. Båda PHP2Shell-luckorna sätter in vid minnestillstånd: vid ett out-of-memory-tillstånd under bearbetningen av förfrågan och vid internt tillstånd som blir kvar för länge efter frigörandet.

Hur PHP hanterar minne: överblicken

I språk som C måste programmeraren själv begära och frigöra minne. PHP tar över det. Varje variabel, varje array och varje objekt tar minne i anspråk som PHP hanterar via sin egen minneshanterare. Gränsen för det sätts av memory_limit, enligt manualen som standard 128M.

För beslutsfattare är en punkt central: Vid klassisk drift med PHP-FPM eller mod_php körs långlivade worker-processer som efter varandra bearbetar många förfrågningar. Det en förfrågan lämnar efter sig måste vara pålitligt städat före nästa. Just här ligger en av PHP2Shell-luckorna.

Referensräkning: räkna vem som använder ett värde

PHP lagrar varje värde i en behållare som kallas zval. Vid sidan av typ och innehåll för den enligt manualen en referensräknare (refcount) och en markering om värdet är en del av en PHP-referens med & (is_ref).

<?php
$a = str_repeat('x', 1000);  // ett värde, refcount 1
$b = $a;                     // ingen kopiering, refcount 2
$b .= 'y';                   // först nu kopieras det (copy-on-write)
unset($a);                   // originalets refcount sjunker
// Når en räknare 0 frigör PHP minnet direkt.

Principen heter copy-on-write: PHP kopierar ett värde först när en av variablerna ändrar det. Det sparar mycket minne. En variabels räknare kan synliggöras med tillägget Xdebug och funktionen xdebug_debug_zval(). Manualen påpekar att de exakta talen kan avvika mellan PHP-versioner.

Cykler: när referensräkning inte räcker

Referensräkning har en känd svaghet. Pekar en struktur på sig själv sjunker dess räknare aldrig till noll.

<?php
$a = ['eins'];
$a[] = &$a;   // arrayen innehåller en referens till sig själv
unset($a);    // ingen kommer åt den längre, men refcount förblir 1

Enligt manualen förekommer sådana cirkelreferenser särskilt hos objekt, till exempel i förälder-barn-strukturer eller parsrar. Enstaka läckor är ofarliga, eftersom PHP städar upp i slutet av förfrågan. I långkörande processer som köarbetare eller stora testkörningar kan miljontals av dem dock summeras till ett verkligt minnesproblem.

Cykel-garbage-collectorn

Sedan PHP 5.3.0 använder PHP en algoritm av Bacon och Rajan för att hitta sådana cykler. Grundidén: En cykel kan bara uppstå när en räknare sjunker till ett värde större än noll. Bara de värdena minns PHP som möjliga rötter.

  1. Möjliga rötter hamnar i en buffert. Manualen anger 10 000 poster som standardstorlek; det aktuella tröskelvärdet visar gc_status() i fältet threshold.
  2. Är bufferten full går PHP igenom alla nåbara värden och minskar provisoriskt deras räknare.
  3. I en andra genomgång gäller: Värden med räknare noll är skräp, för alla andra ångras minskningen.
  4. Rötterna tas bort ur bufferten och alla värden som identifierats som skräp frigörs.

Styrningen sker via zend.enable_gc (på som standard) eller vid körning med gc_enable() och gc_disable(). gc_collect_cycles() tvingar fram en körning och returnerar antalet insamlade cykler.

Mäta och iaktta minne

Funktion Syfte
memory_get_usage() minne som skriptet för tillfället har tagit i anspråk
memory_get_usage(true) minne som minneshanteraren har reserverat hos systemet; mot detta värde kontrolleras memory_limit
memory_get_peak_usage() högsta nivån av upptaget minne
gc_status() körningar, insamlade cykler, tröskelvärde, rötter; sedan PHP 8.3.0 även körtider
gc_collect_cycles() utlös cykelinsamling direkt
WeakMap koppling till objekt utan att förhindra att de frigörs, sedan PHP 8.0

Enligt manualen registrerar memory_get_usage() bara minne som PHP begär via sin egen minneshanterare. Det som tillägg hämtar direkt från operativsystemet saknas i de här talen. För minnesgränsen för hela processen ansvarar därför operativsystemet eller containern.

Minneshantering och PHP2Shell

Beskrivningarna av båda luckorna leder direkt in i det här ämnet. Vid CVE-2026-109618 kan ett out-of-memory-tillstånd avbryta PHP medan förfrågningsdata bara är delvis initialiserade; vid städningen kan ogiltiga data då behandlas som giltiga. Vad som internt händer vid ett minnesöverskridande förklarar sidan memory_limit och Out-of-Memory. Vid CVE-2026-109619 kan en förfrågans interna tillstånd finnas kvar efter att minnet har frigjorts och användas av en senare förfrågan i samma worker innan det återställs. Förloppet från start till städning beskriver sidan Request-livscykeln.

Som tillfällig åtgärd rekommenderar php.net att sätta memory_limit=-1 så att applikationer inte kan skriva över det, till exempel med php_admin_value i FPM-poolen, och att i stället begränsa minnet via containrar eller cgroups, inte via RLIMIT_AS eller RLIMIT_DATA. Hur det går med systemd visar inlägget Sätta minnesgränser med systemd och cgroups.

; FPM-pool: tillfällig åtgärd enligt php.net, starta sedan om alla workers
php_admin_value[memory_limit] = -1
Viktigt

Utan gräns via container eller cgroup kan en enskild process belasta servern fullt ut. Och ingen inställning ersätter uppdateringen till PHP 8.5.12, 8.4.27, 8.3.36 eller 8.2.35 inklusive omstart av alla workers, eftersom bara omstarten tar bort gamla processer ur minnet.

Hur hjälper PHP SOS konkret?

  • Vi kontrollerar de gällande minnesinställningarna per pool och om applikationer kan skriva över dem.
  • Vi genomför de tillfälliga åtgärderna med en minnesgräns på container- eller cgroup-nivå och testar belastningen.
  • Vi installerar uppdateringen, startar om alla workers och visar att inga gamla processer längre körs, inom ramen för vår PHP-patchtjänst.
  • Vi analyserar minnesfel och avbrutna workers i loggarna från och med den 20 oktober 2026.

Vanliga frågor

Vad är referensräkning i PHP?

PHP räknar för varje värde hur många variabler eller element som pekar på det. Sjunker räknaren till noll frigörs minnet direkt. På så sätt återlämnas det mesta minnet utan en egen städrunda.

Varför behöver PHP då ändå en garbage collector?

För cykler. Pekar en array på sig själv eller känner två objekt varandra sjunker räknaren aldrig till noll, även om ingen längre kommer åt dem. Cykel-garbage-collectorn hittar och tar bort sådana strukturer.

Frigör PHP minnet i slutet av en förfrågan?

Ja, minne som ett skript har tagit i anspråk under en förfrågan städar PHP upp i slutet av förfrågan. Vid PHP2Shell gäller CVE-2026-109619 dock just fallet att internt tillstånd kan finnas kvar efter frigörandet och användas av en senare förfrågan i samma worker.

Varför rekommenderar php.net memory_limit=-1 som tillfällig åtgärd?

Sårbarheten CVE-2026-109618 hänger ihop med ett out-of-memory-tillstånd. php.net rekommenderar att sätta memory_limit=-1 så att applikationer inte kan skriva över det, och att i stället begränsa minnet via containrar eller cgroups. Det är inget fullständigt skydd och ersätter inte uppdateringen.

Källor

Direktkontakt

Varje timme räknas. Skriv till oss nu.

Beskriv kort system, PHP-version och observation. Vi hör av oss, klarlägger läget och arbetar på distans tills incidenten är avslutad och dokumenterad.

Direkthjälp