Únik paměti - Memory leak

Ve výpočetní technice je nevracení paměti typ úniku prostředků , ke kterému dochází, když počítačový program nesprávně spravuje přidělení paměti tak, že se uvolní již nepotřebná paměť. K nevracení paměti může také dojít, když je objekt uložen v paměti, ale nelze k němu získat přístup spuštěným kódem. Únik paměti má příznaky podobné řadě dalších problémů a obecně jej může diagnostikovat pouze programátor s přístupem ke zdrojovému kódu programu.

K úniku prostoru dochází, když počítačový program využívá více paměti, než je nutné. Na rozdíl od nevracení paměti, kde se nevrácená paměť nikdy neuvolní, se uvolní paměť spotřebovaná únikem místa, ale později, než se očekávalo.

Protože mohou při běhu aplikace vyčerpávat dostupnou systémovou paměť, jsou úniky paměti často příčinou stárnutí softwaru nebo k němu přispívají .

Důsledky

Nevracení paměti snižuje výkon počítače snížením množství dostupné paměti. Nakonec může v nejhorším případě být přiděleno příliš mnoho dostupné paměti a celý systém nebo část zařízení nebo jeho část přestane správně fungovat, aplikace selže nebo se systém výrazně zpomalí v důsledku mlácení .

Úniky paměti nemusí být vážné nebo dokonce zjistitelné běžnými prostředky. V moderních operačních systémech se po ukončení aplikace uvolní normální paměť používaná aplikací. To znamená, že únik paměti v programu, který běží jen krátkou dobu, nemusí být zaznamenán a je zřídka vážný.

Mezi mnohem vážnější úniky patří:

  • kde program běží delší dobu a v průběhu času spotřebovává další paměť, jako jsou úlohy na pozadí na serverech, ale zejména ve vestavěných zařízeních, která mohou být spuštěna po mnoho let
  • kde je často přidělována nová paměť pro jednorázové úkoly, například při vykreslování snímků počítačové hry nebo animovaného videa
  • kde program může požadovat paměť - například sdílenou paměť  -, která není uvolněna, i když program skončí
  • kde je paměť velmi omezená, například ve vestavěném systému nebo přenosném zařízení, nebo kde program vyžaduje pro začátek velmi velké množství paměti, přičemž ponechává malý prostor pro únik
  • kde k úniku dochází v rámci operačního systému nebo správce paměti
  • když ovladač systémového zařízení způsobí únik
  • běžící na operačním systému, který při ukončení programu automaticky neuvolní paměť.

Příklad úniku paměti

Následující příklad, napsaný v pseudokódu , má ukázat, jak může dojít k nevracení paměti a její efekty, aniž byste potřebovali znalosti programování. Program je v tomto případě součástí velmi jednoduchého softwaru určeného k ovládání výtahu . Tato část programu se spustí vždy, když někdo uvnitř výtahu stiskne tlačítko pro podlahu.

When a button is pressed:
  Get some memory, which will be used to remember the floor number
  Put the floor number into the memory
  Are we already on the target floor?
    If so, we have nothing to do: finished
    Otherwise:
      Wait until the lift is idle
      Go to the required floor
      Release the memory we used to remember the floor number

K úniku paměti by došlo, pokud by požadované číslo patra bylo stejné jako na kterém je výtah; podmínka pro uvolnění paměti by byla přeskočena. Pokaždé, když k tomuto případu dojde, unikne více paměti.

Takové případy by obvykle neměly žádné okamžité účinky. Lidé často nestiskají tlačítko na podlaze, na které již jsou, a v každém případě může mít výtah dostatek volné paměti, že se to může stát stokrát nebo tisíckrát. Výtahu však nakonec dojde paměť. To může trvat měsíce nebo roky, takže to nemusí být objeveno navzdory důkladnému testování.

Následky by byly nepříjemné; přinejmenším by výtah přestal reagovat na žádosti o přesun do jiného patra (například když se pokusí zavolat výtah nebo když je někdo uvnitř a stiskne tlačítka na podlaze). Pokud jiné části programu potřebují paměť (například část přiřazená k otevírání a zavírání dveří), pak by nikdo nemohl vstoupit, a pokud je někdo náhodou uvnitř, uvízne v pasti (za předpokladu, že dveře nelze otevřeno ručně).

Nevracení paměti trvá, dokud není systém resetován. Například: pokud by bylo napájení výtahu vypnuto nebo došlo k výpadku proudu, program by se zastavil. Když bylo napájení znovu zapnuto, program se restartoval a veškerá paměť byla znovu k dispozici, ale pomalý proces úniku paměti by se restartoval společně s programem, což by nakonec ohrozilo správný chod systému.

Únik ve výše uvedeném příkladu lze napravit přenesením operace 'uvolnění' mimo podmíněné:

When a button is pressed:
  Get some memory, which will be used to remember the floor number
  Put the floor number into the memory
  Are we already on the target floor?
    If not:
      Wait until the lift is idle
      Go to the required floor
  Release the memory we used to remember the floor number

Problémy s programováním

Nevracení paměti je běžnou chybou při programování, zejména při používání jazyků , které nemají zabudované automatické shromažďování odpadků , jako je C a C ++ . K nevracení paměti obvykle dochází, protože dynamicky přidělená paměť se stala nedostupnou . Prevalence chyb v úniku paměti vedla k vývoji řady ladicích nástrojů pro detekci nedostupné paměti. BoundsChecker , Deleaker , IBM Rational Purify , Valgrind , Parasoft Insure ++ , Dr. Memory a memwatch jsou některé z populárnějších debuggerů paměti pro programy C a C ++. „Konzervativní“ možnosti sbírání odpadků lze přidat do jakéhokoli programovacího jazyka, kterému chybí jako integrovaná funkce, a knihovny pro tento účel jsou k dispozici pro programy C a C ++. Konzervativní sběratel najde a získá většinu, ale ne všechny, nedosažitelné paměti.

Přestože správce paměti může obnovit nedostupnou paměť, nemůže uvolnit paměť, ke které je stále dosažitelný, a proto potenciálně stále užitečný. Moderní správci paměti proto poskytují programátorům techniky pro sémantické značení paměti s různými úrovněmi užitečnosti, které odpovídají různým úrovním dosažitelnosti . Správce paměti neuvolní objekt, který je silně dosažitelný. Objekt je silně dosažitelný, pokud je dosažitelný buď přímo silnou referencí, nebo nepřímo řetězcem silných odkazů. ( Silný odkaz je odkaz, který na rozdíl od slabého odkazu brání tomu, aby byl objekt sbírán odpadky.) Aby tomu zabránil, je vývojář zodpovědný za vyčištění odkazů po použití, obvykle nastavením odkazu na null, jakmile již není. potřebné, a pokud je to nutné, odhlášením všech posluchačů událostí, které udržují silné odkazy na objekt.

Obecně je automatická správa paměti pro vývojáře robustnější a pohodlnější, protože nepotřebují implementovat rutiny uvolňování nebo si dělat starosti se sekvencí, ve které se čištění provádí, nebo se zajímat o to, zda je objekt stále odkazován. Pro programátora je snazší poznat, kdy již není potřeba reference, než vědět, kdy již na objekt není odkazováno. Automatická správa paměti však může způsobit režii výkonu a neodstraňuje všechny chyby programování, které způsobují nevracení paměti.

RAII

RAII , zkratka pro Resource Acquisition Is Initialization , je přístup k problému běžně používanému v C ++ , D a Ada . Zahrnuje přidružení rozsahových objektů k získaným prostředkům a automatické uvolnění prostředků, jakmile jsou objekty mimo rozsah. Na rozdíl od sbírání odpadků má RAII tu výhodu, že ví, kdy objekty existují a kdy neexistují. Porovnejte následující příklady C a C ++:

/* C version */
#include <stdlib.h>

void f(int n)
{
  int* array = calloc(n, sizeof(int));
  do_some_work(array);
  free(array);
}
// C++ version
#include <vector>

void f(int n)
{
  std::vector<int> array (n);
  do_some_work(array);
}

Verze C, jak je implementována v příkladu, vyžaduje explicitní deallokaci; pole je dynamicky přidělováno (z haldy ve většině implementací C) a nadále existuje, dokud není výslovně uvolněno.

Verze C ++ nevyžaduje explicitní deallokaci; vždy k tomu dojde automaticky, jakmile se objekt arraydostane mimo rozsah, včetně případů, kdy je vyvolána výjimka. Tím se zabrání některým režijním nákladům na systémy sběru odpadků . A protože ničitelé objektů mohou uvolňovat jiné zdroje než paměť, RAII pomáhá předcházet úniku vstupních a výstupních zdrojů, ke kterým se přistupuje pomocí popisovače , což sběr odpadků označování a zametání neřeší elegantně. Patří sem otevřené soubory, otevřená okna, upozornění uživatelů, objekty v knihovně grafických výkresů, primitiva synchronizace vláken, jako jsou důležité sekce, síťová připojení a připojení k registru Windows nebo jiné databázi.

Správné používání RAII však není vždy snadné a má svá úskalí. Pokud si například nejste opatrní, je možné vytvářet visící ukazatele (nebo odkazy) vrácením dat podle odkazu, pouze aby byla tato data odstraněna, když jeho obsahující objekt přejde mimo rozsah.

D používá kombinaci RAII a garbage collection, využívající automatické ničení, když je jasné, že k objektu nelze přistupovat mimo jeho původní rozsah, a garbage collection jinak.

Počítání referencí a cyklické reference

Modernější schémata shromažďování odpadků jsou často založena na pojmu dosažitelnosti - pokud nemáte použitelný odkaz na příslušnou paměť, lze ji shromáždit. Další schémata shromažďování odpadků mohou být založena na počítání odkazů , kde je objekt zodpovědný za sledování toho, kolik odkazů na něj směřuje. Pokud počet klesne na nulu, očekává se, že se objekt uvolní a umožní regeneraci jeho paměti. Chybou tohoto modelu je, že si neví rady s cyklickými referencemi, a proto je dnes většina programátorů připravena přijmout zátěž nákladnějších značek a typů systémů.

Následující kód jazyka Visual Basic ilustruje nevracení paměti kanonického počítání odkazů:

Dim A, B
Set A = CreateObject("Some.Thing")
Set B = CreateObject("Some.Thing")
' At this point, the two objects each have one reference,

Set A.member = B
Set B.member = A
' Now they each have two references.

Set A = Nothing   ' You could still get out of it...

Set B = Nothing   ' And now you've got a memory leak!

End

V praxi by byl tento triviální příklad okamžitě spatřen a opraven. Ve většině skutečných příkladů cyklus referencí zahrnuje více než dva objekty a je obtížnější ho detekovat.

Známý příklad tohoto druhu úniku se dostal do popředí zájmu o vzestup technik programování AJAX ve webových prohlížečích v problému s odpadlým posluchačem . JavaScriptový kód, který by přidružoval prvek DOM k obsluze událostí a nedokázal odstranit referenci před ukončením, by způsobil únik paměti (webové stránky AJAX udržují daný DOM naživu mnohem déle než tradiční webové stránky, takže tento únik byl mnohem zjevnější) .

Efekty

Pokud má program nevracení paměti a jeho využití paměti se neustále zvyšuje, obvykle se nejedná o okamžitý příznak. Každý fyzický systém má omezené množství paměti, a pokud únik paměti není obsažen (například restartováním unikajícího programu), bude to nakonec způsobovat problémy.

Většina moderních operačních systémů pro stolní počítače má jak hlavní paměť, která je fyzicky umístěna v mikročipech RAM, tak sekundární úložiště , jako je pevný disk . Přidělení paměti je dynamické - každý proces získá tolik paměti, kolik vyžaduje. Aktivní stránky jsou přenášeny do hlavní paměti pro rychlý přístup; neaktivní stránky se přesouvají do sekundárního úložiště, aby se podle potřeby uvolnilo místo. Když jeden proces začne spotřebovávat velké množství paměti, obvykle zabírá stále více hlavní paměti a tlačí ostatní programy do sekundárního úložiště - obvykle výrazně zpomaluje výkon systému. I když je uniklý program ukončen, může chvíli trvat, než se jiné programy vrátí zpět do hlavní paměti a než se výkon vrátí do normálu.

Když je veškerá paměť v systému vyčerpána (ať už je virtuální paměť nebo pouze hlavní paměť, například ve vestavěném systému), jakýkoli pokus o přidělení větší paměti se nezdaří. To obvykle způsobí, že se program pokouší přidělit paměť, aby se sám ukončil, nebo vygeneroval chybu segmentace . Některé programy jsou navrženy tak, aby se z této situace vzpamatovaly (pravděpodobně pádem do předem rezervované paměti). První program, který zaznamenal nedostatek paměti, může, ale nemusí být programem, který má nevracení paměti.

Některé víceúlohové operační systémy mají speciální mechanismy, které se zabývají stavem bez paměti, například náhodným zabíjením procesů (což může ovlivnit „nevinné“ procesy) nebo zabíjením největšího procesu v paměti (který je pravděpodobně příčinou problém). Některé operační systémy mají limit paměti na proces, aby se zabránilo tomu, aby některý program zablokoval veškerou paměť v systému. Nevýhodou tohoto uspořádání je, že operační systém někdy musí být znovu nakonfigurován tak, aby umožňoval správnou činnost programů, které legitimně vyžadují velké množství paměti, například těch, které se zabývají grafikou, videem nebo vědeckými výpočty.

Vzorec využití paměti „pilovým zubem“: náhlý pokles použité paměti je kandidátským příznakem úniku paměti.

Pokud je v jádře nevracení paměti , samotný operační systém pravděpodobně selže. Počítače bez propracované správy paměti, jako jsou vestavěné systémy, mohou také zcela selhat z důvodu trvalého úniku paměti.

Veřejně přístupné systémy, jako jsou webové servery nebo směrovače, jsou náchylné k útokům typu odmítnutí služby, pokud útočník zjistí sled operací, které mohou způsobit únik. Taková sekvence je známá jako exploit .

Vzorec využití paměti „pilovým zubem“ může být indikátorem úniku paměti v aplikaci, zvláště pokud se svislé poklesy shodují s restartováním nebo restartováním této aplikace. Je však třeba být opatrný, protože body pro sběr odpadu by také mohly způsobit takový vzorec a ukazovaly by zdravé využití hromady.

Ostatní spotřebitelé paměti

Neustálé zvyšování využití paměti nemusí být nutně důkazem nevracení paměti. Některé aplikace budou ukládat stále větší množství informací do paměti (např. Jako mezipaměť ). Pokud se mezipaměť může zvětšit tak, že způsobuje problémy, může se jednat o chybu programování nebo návrhu, ale nejedná se o nevracení paměti, protože informace zůstávají nominálně používány. V jiných případech mohou programy vyžadovat nepřiměřeně velké množství paměti, protože programátor předpokládá, že paměť je pro konkrétní úkol vždy dostačující; procesor procesoru grafických souborů může například začít čtením celého obsahu obrazového souboru a jeho uložením do paměti, což je něco, co není životaschopné tam, kde velmi velký obrázek přesahuje dostupnou paměť.

Jinak řečeno, k úniku paměti dochází z určitého druhu chyby programování a bez přístupu k programovému kódu může někdo, kdo vidí příznaky, pouze hádat, že může dojít k úniku paměti. Bylo by lepší použít výrazy jako „neustále se zvyšující využití paměti“ tam, kde takové vnitřní znalosti neexistují.

Jednoduchý příklad v C

Následující funkce C záměrně uvolňuje paměť ztrátou ukazatele na přidělenou paměť. Lze říci, že k úniku dochází, jakmile se ukazatel „a“ dostane mimo rozsah, tj. Když se function_which_allocates () vrátí bez uvolnění „a“.

#include <stdlib.h>

void function_which_allocates(void) {
    /* allocate an array of 45 floats */
    float *a = malloc(sizeof(float) * 45);

    /* additional code making use of 'a' */

    /* return to main, having forgotten to free the memory we malloc'd */
}

int main(void) {
    function_which_allocates();

    /* the pointer 'a' no longer exists, and therefore cannot be freed,
     but the memory is still allocated. a leak has occurred. */
}

Viz také

Reference

externí odkazy