Únik paměti

Únik paměti (anglicky memory leak) označuje v informatice situaci, kdy počítačový program neúmyslně alokuje operační paměť a není ji schopen uvolnit poté, co ji již dále ani nepotřebuje ani nevyužívá. Únik paměti nastává vinou chyby v programu, která uvolnění již nevyužívané paměti zabraňuje. Negativní vliv se zvyšuje, pokud dochází k opakovanému úniku paměti, což může způsobit zřetelné zpomalení počítače nebo dokonce až vyčerpání veškeré dostupné paměti (OOM), které může nakonec způsobit násilné ukončení programů nebo i fatální selhání počítače. V reálném světě by se únik paměti dal přirovnat k člověku, který použité věci neustále uschovává, i když je už nikdy nebude potřebovat.

Za únik paměti jsou někdy označovány i jiné situace (viz níže), avšak takové označení není přesné (například když program jen vyžaduje pro svůj běh neadekvátní množství paměti). Oprava úniku paměti může být většinou diagnostikována a provedena pouze programátorem s přístupem ke zdrojovému kódu chybného programu.

Příčiny

Při programování jsou úniky paměti poměrně běžnou chybou, obzvlášť v případě programovacích jazyků, neobsahujících zabudovanou automatickou správu paměti, jako jsou například C a C++. Programátor musí v těchto jazycích zajistit uvolňování veškeré alokované paměti vlastním kódem, a proto k chybám a opomenutím dochází. Chybějící automatickou správu paměti je však možné i do těchto programovacích jazyků doplnit použitím vhodných knihoven.

Automatická správa paměti

Proti paměťovým únikům nejsou imunní ani jazyky vybavené automatickou správou paměti (garbage collector), jako jsou Java, C#, VB.NET nebo LISP. Jednoduchým příkladem budiž seznam, do něhož program vkládá data, ale po použití je již nesmaže, i když je už dále nebude potřebovat. Garbage collector sám nepozná, jestli program ještě bude v budoucnu uložená data znovu používat. Proto musí sám program indikovat další nepotřebnost uložených dat. Obvykle se to dělá tak, že program řekne "dealokuj!" nebo když je líný, tak spoléhá na to, že jsou odstraněny všechny odkazy (reference) na daná data (resp. na použité části paměti).

Automatická správa paměti uvolňuje pro další použití již nedosažitelnou a tedy zbytečně alokovanou paměť. Nemůže však uvolnit paměť, která obsahuje data stále dosažitelná a potenciálně využitelná. Moderní správa paměti proto poskytuje programátorům možnost sémanticky označit paměť s různou úrovní užitečnosti, odpovídající její dosažitelnosti. Správce tak neuvolní silně[kdo?] dosažitelný objekt, což znamená objekt dosažitelný přímo silnou[kdo?] referencí, nebo nepřímo několika silnými referencemi (silná reference na rozdíl od slabé brání garbage collectoru odstranit odkazovaný objekt). Takovémuto úniku paměti musí zabránit programátor – obvykle nastavením dané reference na null v okamžiku, kdy již není dále potřeba, a případně odregistrováním všech případných event listenerů, udržujících na objekt silnou referenci.

Automatická správa paměti je pro vývojáře obecně robustnější a spolehlivější, neboť není třeba implementovat paměť uvolňující rutiny (jako kdyby nebyly v garbage collektoru) či starat se o případné zbylé reference na čištěné objekty. Pro programátora je jednodušší hlídat, kdy není reference již víc potřebná, než zda už objekt není referencován. Na druhou stranu automatická správa může zvyšovat náročnost na výkon systému a neumí odstranit všechny programové chyby, způsobující únik paměti.

Zjišťování úniků paměti

Typický případ úniku nastává ve chvíli, kdy se dynamicky alokovaná paměť stane nedosažitelnou. Častý výskyt paměťových úniků vedl k vývoji různých ladících nástrojů, detekujících nedosažitelné části paměti. Pro programy v jazyce C a C++ jsou nejpopulárnějšími paměťovými debuggery IBM Rational Purify, BoundsChecker, Valgrind, Insure++ a memwatch.

Důsledky

Menší úniky paměti nemusí být závažné nebo ani detekovatelné běžnými prostředky. V moderních operačních systémech je veškerá paměť používaná aplikací uvolněna, jakmile je aplikace ukončena. To znamená, že únik paměti v programu běžícím pouze krátkou dobu má zřídkakdy vážné důsledky.

Začne-li jednotlivý proces zabírat velké množství paměti, obvykle obsadí většinu RAM a „utlačením“ ostatních procesů do virtuální paměti na pevném disku významně zpomalí výkon celého systému. I po jeho ukončení ostatním programům trvá poměrně dlouhou dobu, než se odložená paměť dostane zpět do hlavní paměti a tím se činnost systému vrátí do původního stavu.

Vážný únik paměti může mimo již zmíněný efekt na rychlost celého systému ovlivnit i jednotlivé další aplikace, neboť programátor obvykle nepředpokládá, že by požadovaná paměť nebyla k dispozici a program se s tím neumí vyrovnat.

Kritické úniky

Když je celá paměť (ať už virtuální či pouze RAM, jako v případě embedded systémů) vyčerpána, selže každý další pokus o alokování paměti, což obvykle způsobí ukončení alokujícího programu, případně generování chyby ochrany paměti. Některé programy jsou navrženy tak, aby se z takové situace dokázaly zotavit. Ovšem program, který jako první prodělá nedostatek paměti, nemusí vždy být původcem úniku paměti.

Pokud útočník objeví operaci, která způsobuje únik paměti u veřejně přístupných systémů (webové servery, routery), může je zneužít při útoku typu DoS (odmítnutí služby). Jedná se o techniku útoku na internetové služby nebo stránky, při níž dochází k přehlcení požadavky, velké spotřebě zdrojů a následně pádu nebo minimálně nefunkčnosti a nedostupnosti pro ostatní uživatele.

Případy vážných úniků

  • program spuštěn dlouho a spotřebovává stále více a více paměti (např. démon, který je spuštěn na pozadí nebo ve vestavěných zařízeních, která mohou běžet nepřetržitě několik let)
  • paměť je alokována často (např. při renderování snímků počítačové hry či animovaného videa)
  • alokovaná paměť není uvolněna, ani když je program ukončen (např. sdílená paměť)
  • k úniku paměti dochází uvnitř operačního systému (např. v ovladači zařízení)
  • paměť systému je malá (např. ve vestavěném systému nebo přenosném zařízení)
  • program, ve kterém nastane únik, běží pod operačním systémem (např. AmigaOS), kde paměť nemusí být při ukončení programu navrácena a lze ji navrátit pouze restartováním celého systému

Laický příklad

Tento příklad napsaný v pseudokódu má znázornit, jak může únik paměti vzniknout a nastínit jeho vliv, to vše bez jakékoliv znalosti programování.

Program je v tomto případě částí velmi jednoduchého softwaru určeného pro ovládání výtahu. Tato část programu je spuštěna vždy, když někdo uvnitř výtahu stiskne tlačítko požadovaného patra.

Bylo stisknuto tlačítko:
Vezmi část paměti, která bude použita na zapamatování čísla patra
Dej číslo patra do paměti
Jsi již v cílovém patře?
Pokud ano, není co dělat: hotovo
Jinak:
Počkej, dokud nebude výtah v klidu
Jeď do požadovaného patra
Uvolni paměť používanou k zapamatování čísla patra

K úniku paměti dojde, když zvolené číslo patra bude stejné jako to, ve kterém se výtah právě nachází; podmínka pro uvolnění paměti bude přeskočena. Pokaždé, když nastane tento případ, navýší se objem uniklé paměti.

Případy jako tento většinou nemají žádný bezprostřední efekt. Lidé nemačkají tlačítko pro patro ve kterém právě jsou příliš často. V každém případě, výtah bude mít dost paměti na to, aby se toto mohlo stát stokrát nebo i tisíckrát. Nicméně, nakonec výtahu paměť dojde. To může trvat měsíce nebo roky, takže to nemusí být objeveno ani relativně důkladným testováním.

Následky budou v tomto případě nepříjemné; výtah přinejmenším přestane odpovídat na žádosti o cestu do jiného patra. Pokud program potřebuje paměť na otevření výtahových dveří, pak může někdo zůstat uvězněn uvnitř, protože výtah nebude schopen dveře bez paměti otevřít.

Únik paměti bude přetrvávat tak dlouho, dokud poběží program. Například pokud se výtah odpojí od elektrické sítě, systém se vypne. Jakmile je výtah opět zapojen, systém znovu nastartuje, paměť bude znovu dostupná a pomalý proces unikání začne znovu.

Úniky paměti a operační systém

Jak už bylo zmíněno, příznakem paměťového úniku je narůstající množství paměti využité aplikací. Tento nárůst nemusí být nijak výrazný ani pravidelný, a proto je obtížné objevit paměťové úniky u aplikací, které se spouštějí na krátkou dobu. V případě dlouhodobě spuštěných programů je otázkou času, kdy se únik projeví a vzhledem k omezené velikosti dostupné paměti počítače začne způsobovat problémy.

Moderní operační systémy přidělují jednotlivým procesům virtuální paměťový prostor, do kterého na základě jeho požadavků dynamicky přidělují bloky operační paměti. Zároveň sledují aktivitu spuštěných aplikací a na jejím základě a podle množství volné paměti řídí přesuny stránek mezi operační pamětí a pevným diskem.

V případě, kdy nějaká aktivní aplikace požaduje stále více paměti, způsobuje odsuny stránek ostatních aplikací (nebo i vlastní) na pevný disk, který v klasickém provedení naprosto není vhodný na velké množství náhodných přístupů k malým blokům dat, což se s daty v operační paměti děje běžně. To způsobuje zhoršení odezvy systému, která může dosáhnout takové úrovně, že se stane nepoužitelným. I po ukončení (nebo restartování) problémové aplikace trvá určitou dobu, než se stránky znovu vrátí do operační paměti a odezva systému klesne na původní úroveň.

Pokud únik paměti pokračuje dál, může se stát, že dojde ke spotřebování celé dostupné paměti (out of memory, zkratka OOM), včetně případné virtuální. Každý další požadavek na přidělení paměti končí neúspěchem, a to pro všechny aplikace v systému. Chyba přidělení paměti způsobí v lepším případě ukončení aplikace (a uvolnění její paměti pro ostatní) nebo zobrazení chybového hlášení. V horším případě může vést k neočekávanému vnitřnímu stavu aplikace s případnou ztrátou nebo poškozením dat.

I v případě, že se aplikace při nedostatku paměti ukončují, není zaručeno, že se ukončí právě aplikace způsobující únik paměti, dokud je aktivní ještě jiná aplikace. Operační systém může obsahovat algoritmy, které se pokoušejí tuto situaci řešit (tzv. OOM killer) například ukončením procesu s největší alokovanou pamětí nebo „náhodným“ ukončováním aplikací, aby se udržela nějaká volná paměť k přidělení.

Dalším řešením je limit velikosti dostupné paměti pro jednotlivé procesy, kdy obsazení tohoto prostoru nezpůsobí nedostupnost paměti pro ostatní aplikace. Toto řešení má ale nevýhodu v nutnosti zvlášť konfigurovat systém, pokud některá aplikace potřebuje větší množství paměti, než je běžné maximum (například pro práci s obrazem nebo na vědecké výpočty).

Pokud únik paměti nastane v jádru operačního systému (například zavedením vadného ovladače), je pravděpodobné, že se systém dostane do větších potíží vedoucích až k zatuhnutí systému.

Jiné příčiny spotřebovávání paměti

Pokud aplikaci s narůstající dobou provozu narůstá i množství spotřebované paměti, nemusí se vždy jednat o únik paměti. Některé aplikace si za běhu ukládají informace a neregulují jejich množství.

Jako jednoduchý příklad může sloužit aplikace sledující dostupnost webové stránky. Pokud si v pravidelných časových intervalech zaznamená čas a dostupnost stránky bude bez dalších úprav spotřeba paměti s dobou provozu narůstat i přesto, že se nejedná o únik paměti.

Následky takových aplikací jsou stejné jako v případě úniků paměti, jenom se nejedná o chybu v programu, ale o program nevhodně navržený. Případně se může jednat i o program, který načte velké množství dat (například editor obrázků) a zaplní tak operační paměť během chvilky a i přesto, že paměťová náročnost s dobou provozu neporoste, další příznaky budou podobné (pomalá odezva systému, aplikace využívající všechnu dostupnou paměť). Tedy i přesto že se aplikace chová jako by obsahovala únik paměti, můžeme bez znalosti zdrojových kódů jenom odhadovat jestli se o něj skutečně jedná.

Reference

V tomto článku byl použit překlad textu z článku Memory leak na anglické Wikipedii.