Zabraná paměť
Školení, která pořádám
V PHP je k dispozici funkce memory_get_usage, která vrátí velikost zabrané paměti. Stejnou hodnotu používá i konfigurační direktiva memory_limit pro omezení maximálního množství paměti zabrané skriptem. Určitou paměť ale také zabírají knihovny, které PHP používá, tato funkce ji ale nezapočítává.
Pod Windows XP v PHP 5.2.5 a pod Linuxem s jádrem 2.6.20.4-grsec v PHP 5.2.0-8+etch7 jsem spustil následující jednoduchý skript a měřil jsem množství zabrané paměti podle PHP a celkem v operačním systému:
<?php
$tabulka = array();
$result = mysql_query("SELECT * FROM tabulka");
while ($row = mysql_fetch_assoc($result)) {
$tabulka[] = $row;
}
mysql_free_result($result);
$kopie = $tabulka;
unset($kopie[0]);
unset($kopie, $tabulka);
?>
Okamžik | podle PHP | podle Windows | podle Linuxu |
začátek | 61 kB | 11724 kB | 8484 kB |
položení dotazu | +0 kB | +2256 kB | +2568 kB |
průchod cyklem | +8665 kB | +8512 kB | +14052 kB |
uvolnění dotazu | –0 kB | –2188 kB | –2428 kB |
zkopírování proměnné | +0 kB | +8 kB | +4 kB |
odnastavení prvku pole | +305 kB | +304 kB | +564 kB |
odnastavení proměnných | –8905 kB | –8188 kB | –14144 kB |
Datový soubor s tabulkou měl 2 MB. Je vidět, že už samotné položení dotazu stáhne data z databáze (na rozdíl od mysql_unbuffered_query), tato jsou ale uložena knihovnou. Následující uložení tabulky do pole zabere přes 8 MB paměti (ukládají se i klíče pole a něco také zabere organizace pole), jeho zkopírování do druhé proměnné nezabere nic. PHP totiž data kopíruje, až když to je potřeba. K tomu dojde při odnastavení prvního prvku druhého pole, které tím pádem paměť zabere, místo aby ji ušetřilo. Odnastavení obou proměnných vrátí skoro všechnu zabranou paměť.
Obvykle se o množství zabrané paměti není potřeba příliš starat, ale je dobré mít alespoň představu, kdy se kolik paměti zhruba zabírá.
Přijďte si o tomto tématu popovídat na školení Výkonnost webových aplikací.
Diskuse
Bohdan:
Neznáte nějaký způsob, jak zmenšit obrázek větších rozměrů, když to nejde přes gd kvůli malému memory_limitu a imagemagick není k dispozici? Pro většinu formátů je možné obrázek přímo dekomprimovat do menšího rozlišení, zmenšování by pak nezabralo tolik paměti. Vlastně zvažuji, že bych se do toho pustil, nějaké zmenšování uploadovaných obrázků je potřeba skoro všude.
Víte u nás o nějakém dobrém hostingu kde by to nebyl problém?
ATom:
No, tohle by mě taky zajímalo, gd knihovna celý obazek převede do bitmapy a to je při dnešních rorměrech uploadovaných fotek doslova katastrofa.
Hds:
Tohle by mě taky moc zajímalo...
Tom:
Jisteze, nepouzivejte bezne webhostingy ale pro tento web s rozsahlou galerii uzijte vlastni server. Pak nebudete mit problem tam mit spusteny s nizkou prioritou imagemagick, ktery vam je bude davkove zmensovat a s citelne nizsi reziji nez php.
Nick:
To je sice krasna rada, ale k nicemu. Zakaznik ma web o par strankach a do galerie chce obcas vlozit nejakou aktualni fotku. Jelikoz ma zrcadlo, tak o velikosti 3000*2000px. Nechce instalovat a ucit se ovladat zadne graficke programy. Proste fotka z fotaku nejkratsi cestou primo na web. A na webu to ma byt velke 1024*681px. Vlastni server neprichazi v uvahu (kdo to zaplati?). Hosting ma nastavene nizky memory_limit, protoze dle vyjadreni podpory "je nastaveny tak, aby vyhovoval naproste vetsine zakazniku". Kdo chce vetsi memory_limit, necht zaplati nepomerne vetsi pausal. Mate nejakou radu ted?
Leda mu na komplu nainstalovat phpko a udělat nějaký baťák, který spustí php skript, který zmenší fotku...
To neni zrovna elegantní řešení. to už je lepší použít irfan.
Ono by to mělo nějak jít integrovat s průzkumníkem a pak je to sranda. Jen mu nesmí vadit to černé okénko.
Stejně mi přijde nesmyslný instalovat klientovi na osobní počítač Apache server s PHP jen pro zmenšování fotek.
Na co Apache? Stačí php spouštěné z komadnlajny...
BTW: Instalovat JRE nikomu divný nepřijde, je divné instalovat php?
Juraj:
Tak jedine použiť IMagick ten má menšie pamaťové nároky ako knižnica GD.
Přenášet obrázek 3000x2000 bodů je nešikovné nejen kvůli zabrané paměti, ale i kvůli tomu, že to dlouho trvá. Pokud nechce žádný externí program, tak by asi byla ideální nějaká Flashová komponenta, která by to zmenšila u klienta a na server poslala už malé.
Juraj:
Každý dobrý hosting však poskytuje GD a prípadne IMagick.
ATom:
A odkud je přesně brány ten nárust paměti procesu? Přímo z procesu apache serveru (PHP) nebo z celkové dostupné paměti ve Windows?. Protože už při položení dotazu si podle mě bez ohledu na to, co dělá mysql_query() načte do cache ta data samotná MySQL a v systému klesne paměť.
Skript jsem spouštěl z příkazové řádky, nikoliv přes Apache. Paměť zabranou MySQL jsem nezohledňoval, to může běžet na jiném serveru. Jde tedy o paměť zabranou procesem PHP.
Bohdan:
Takže pokud pameť zabírá nějaká knihovna, nedá se rozlišit jaký skript to má na svědomí? Tedy nejde ani omezit skutečnou velikost zabrané paměti na skript?
N. J.:
Mohl bys Havrane zase neco noveho napsat? Ja uz se docela nudim, na CZ/SK web scene je to samy sucker, proto nemam co cist.
bylo by asi zajimavejsi cely skript testovat na linuxu, server s php na windowsech (a jeste k tomu win xp) asi ze serioznich hostingu neprovozuje skoro nikdo...
v6ak:
Na Linuxu naměřilo phpko stejně?
Jakub Vrána :
Strukturou ano, v absolutních číslech o něco výš:
80
+0
+14564
–0
+0
+564
–14669
v6ak:
Já mluvím o měření z php, ne z OS.
v6ak:
Tak to je fajn. Nebylo mi to úplně jasné.
Malis:
Už mi bylo sice vysvětleno, že to nejde, ale přesto mi to nedá se nezeptat se i zde, bo je to tu samý expert:
Je mi jasné (a celkem pochopitelné), že nepřinutím skript zabrat více paměti, popř. běžet déle, než je povoleno. Nicméně bohatě by mi stačilo, kdyby se existovalo něco jako "ghost" funkce, která by mi řekla "Ne, ne, ne, tohle spadne, to zabere moc paměti".
A ještě lepší by bylo, kdyby funkce, která se blíží časovému limitu či vymezené paměti, uměla nějak efektně spadnout s nějakou chybovou návratovou hodnotou, se kterou by se dalo nějak rozumně pracovat. Ale to bych asi už chtěl moc.
Protože nevím, že by něco takového bylo, řeším to pomocí omezení, co vlastně program může zpracovávat a nechávám si dostatečnou rezervu. Kdybyste mě ale někdo vyvedl z omylu a prohlásil, že něco takového existuje, hned by na tom světě bylo růžověji...
Pavel Zbytovský:
Myslím, že žádná podobná funkce neexistuje, můžeš to leda zkusit odhadnout, kolik to bude zabírat.
Co se týče ukončení skriptu před vypršením max_timu, tak to takový problém není. Počítáš si čas pomocí microtime a v předpokladaných dlouhotrvajících cyklech si kontrolovat jestli se to neblíží. Zkuste se inspirovat v uploadovací funkci phpmyadmina.
A co se týče paměti, spočítat si kolik bude mít výsledek z databáze tak těžké není a jediný problém tedy je spočítat, kolik bude mít otevřený JPEG - doporučuji diskusi pod manuálovou stránkou: http://php.net/manual/en/function.imagecreatefromjpeg.php - je to tam několikrát a podle vlastní zkušenosti to je použitelné.
PHX:
Jde nejak zjistit kolik pameti dany script zabral maximalne? Jedine co me napada je, dat za kazdy prikaz svoji funkci, ale to je silene.
v6ak:
PHP umí i větvení. AFAIK o tom bylo i na tomto webu. Pokud budeš zaznamenávat vše, musíš nějak odečíst paměť zabranou měřící fcí, která bude narůstat, nebo pokaždé např. otevřít, upravit a zavřít soubor. Pokud budeš uchovávat jen maxmum, mělo by to stačit.
Jen nevím, zda se každému vlánku neměří paměť zvlášť.
Diskuse je zrušena z důvodu spamu.