Pojmem memory leaks se označují stavy, kdy aplikace není schopna uvolnit alokovanou paměť. Nedávno tento problém hlouběji rozebíral Roman -Dagi- Pichlík. A před pár dny jsem zjistil, jak katastrofálně je na tom PHP.
Trošku odbočím. Před každým vypuštěním nové verze Texy! ji testuji
tak, že nechám převést několik tisíc připravených souborů a
porovnávám získané výsledky s referenčními. Onehdy jsem během
testování měl spuštěného Správce úloh, záložku Procesy. Řeknu vám,
pěkně mi klesla čelist, když jsem zahlédl, že PHP si ukouslo 900MB RAM a
stále chce víc. Cosi shnilého je v mé aplikaci, pomyslel jsem si, a jal se
hledat problém.
Tak jsem testovací skript + knihovnu Texy! postupně redukoval a redukoval, až jsem to zredukoval na těchto pár řádků:
class MemoryLeakClass {
var $ref;
}
$obj = new MemoryLeakClass();
$obj->ref = & $obj;
unset($obj);
Pokud si myslíte, že příkaz unset() uvolní paměť drženou objektem, jste na omylu. Neuvolní. Můžete si to ověřit tak, že spustíte tento lehce upravený příklad a budete ve Správci úloh sledovat využitou paměť:
class MemoryLeakClass {
var $consumeBigMemory;
var $ref;
}
$obj = new MemoryLeakClass();
// allocate 5MB
$obj->consumeBigMemory = str_repeat('*', 5e6);
$obj->ref = & $obj;
unset($obj);
// wait 10s
sleep(10);
Během čekání (sleep) je krásně vidět, že paměť zůstává blokovaná. To je mazec, co?
Příklad můžete modifikovat i tak, že vytvoříte dva objekty, které budou referencí odkazovat jeden na druhého. I když je společně zrušíte příkazem unset(), vzpomínka na ně zůstane v živé paměti.
Problém se týká prakticky všech verzí PHP, testoval jsem od 4.3.0 do 5.0.4. Prohledal jsem PHP Bugs a našel více než dva roky starý související Bug #22055 Memory leak with references in objects. Reakce jednoho z vývojářů mě dostala:
It won't be fixed in PHP 4 anyway as it has to do with the way objects
Nicméně ani v pětce to vyřešené není.
Nejvíc mě na tom celém trápí, že neznám způsob, jak ty objekty uvolnit. I když zruším $obj->ref a následně $obj, stejně to nepomůže. Takže … nevíte o nějaké učebnici Javy pro začátečníky? 🙂
Komentáře
David Bures #1
v kazdem pripade v J2ME je problem s uvolnovanim pameti pri krizove referenci taky …
martinpav #2
Neviem ci sa jedna o rovnaky pripad, ale mam dojem ze PHP sa chova podobne aj pri spracovavani XML.
Pri zpracovani 15MB suboru pomocou event based parseru si „odkuslo“ cca 220 MB. Moc nadseny som z toho nebol.
Ľuboš Kmeťko #3
Táto neschopnosť uvoľniť alokovanú pamať ma riadne vytrápila v jednom projekte, kde som potreboval dlhodobo bežiaci skript pripájajúci sa na rôzne web stránky a spracúvajúci ich obsah pomocou XML_HTMLSax z PEARu. Nakoniec som bol nutený to obísť periodickým volaním skriptu cez popen z iného skriptu. Skrátka kým skript neukončil beh tak sa pamäť alokovaná (aj) zrušeným objektami nijak nedala uvoľniť a skript nakoniec nakoniec prekročil limit.
Inak na testovanie sa dá použiť aj memory_get_usage()
David Grudl #4
#3 Ľuboši Kmeťko, dá, ale ne ve windows.
Petr Gurth #5
Na diskuzi na https://web.archive.org/…on.unset.php jsem nasel zajimave poznamky, jejichz aplikace funguje, ale…
David Grudl #6
#5 Petře Gurthu, v podstatě takto jsem do Texy! přidal „destruktor“ free(), který zruší všechny proměnné v objektu:
Nicméně po nějakých úpravách v kódu přestal i tento trik fungovat. Pokusím se to opět zredukovat na jednoduchý příklad a dám sem.
Ale s tím
$x = null;
namístounset($x);
je to zajímavý nápad, otestuju to.Petr Gurth #7
Me to prave pomoci unset() nefungovalo a pomoci prirazeni null ano. Prijde mi to divne, nicmene v PHP se uz nedivim nicemu :)
Myshpa #8
V tomhle prikladu vse funguje jak ma (je to kombinace vyse uvedenych prikladu (null a fce free).
Bez volani fce free() je volan destructor az po ukonceni skriptu.
David Grudl #9
Ještě bych rád bych to celé trošku doplnil:
Příkaz
$var = null
odstraní hodnotu, kterou odkazuje proměnná. Pokud hodnotu odkazuje více proměnných spojených referencí, bude ostraněna napříč těmito referencemi. Proměnné jako takové však budou nadále existovat.Příkaz
unset($var)
odstraní proměnnou, neovlivňuje však hodnotu.Pokud neexistuje už žádná proměnná odkazující na hodnotu, mělo by dojít i k uvolnění této hodnoty z paměti. Bohužel, tady PHP občas selhává. V určitých případech je třeba před odstraněním proměnné explicitně odstranit i hodnotu.
Solvina #10
Na javu nepotrebujes ucebnici – mozna neco __velice jednoducheho, na zakladni syntaxi. Pak uz staci naucit se delat s Eclipsem, cist JavaDoc (to mi dalo zabrat nejvic) a vybrat si ze strasny spousty frameworku a zacit realizovat myslenky.
A jo, je mi jasny, ze s tou javou si to myslel jako joke 🙂.
Niceme problem s pameti popisoval i jeden z Krausu (root.cz, woko.cz a buhvico jeste) k dohledani snad na abclinuxu, rootu, nebo jinem sektarskem platku 😉 – https://www.abclinuxu.cz/…lu.cz-v-jave. To co v jave zralo 2MB zralo v php 64MB…
Emo #11
#10 Solvino,
V uvedenem odkazu je toto: Ukázalo se, že na to, co jsem v C zpracoval zhruba ve 2 MB paměti, potřebovalo tehdy PHP neuvěřitelných 64 MB.
Java by si IMHO taky vzala vice nez 2MB.
Ondrej Ivanič #12
Za drvivú väčšinu problémov s referenciami si môžu ludia sami.
Stačí si len uvedomiť, že ak nepotrebujem modifikovť premenú vo funkcii tak je mi referencia zbytočná. PHP používa „copy on write“ a pokiaľ len čítam z premenej tak nenastáva žiadne spomalenie oproti predaniu cez referenciu.
Pri objektoch sa vačšinou predaniu cez referenciu neda vyhnut, ale potom nech je referencia v signature metody/funkcie
A druhá skupina problémov je $this mágia…
char0n #13
Refrencie sa nedaju unsetnut funkciou unset(), ta unsetne
len refrenciu na dane pametove miesto, a pametove miesto ostava stale naplene a uz sa s nim dalej neda pracovat. v PHP 4 je len jedine riesenie:
function _obj_unset(&$obj)
{
$obj = null;
unset($obj);
}
Najprv pametove miesto vycistime a potom unsetneme referenciu na neho.
mic #14
Nevim jestli to sem patří ale taky řešení myslím :
Please note that PHP doesn't have magic callback to unset overloaded properties. This is the reason why unset($SomeObj->Virtual1) doesn't work.
But it does work when we set ‚null‘ value such as the following code:
Tento článek byl uzavřen. Už není možné k němu přidávat komentáře.