Pro někoho termín možná neznámý, pro někoho známý docela dobře. V tomto článku si popíšeme, co to tzv. „garbage collection“ je a k čemu slouží.
Garbage collection je označení pro metodu automatické správy paměti programu (lépe řečeno procesu). Nejdříve ale, než se vůbec budeme moci dostat k samotné podstatě garbage collection, bude potřeba se nejprve seznámit s tím, jak jsou na dnešních běžných počítačích programy v paměti reprezentovány. Počítače, které dennodenně používáme, jsou založeny na tzv. „von Neumannově architektuře“. Tato architektura počítače se stala tak používanou hlavně díky její univerzalitě. Počítač sestavený podle této architektury obsahuje pět logických jednotek: vstupní zařízení, výstupní zařízení, řadič, aritmeticko-logickou jednotku a paměť.
Průběh výpočtu pak vypadá tak, že přes nějaké vstupní zařízení se načte program, který se uloží do paměti (první takovým program bývá jádro operačního sytému), řadič pak předá řízení aritmeticko-logické jednotce a ta pak vykonává tzv. „instrukce“ programu (neboli ta data, která byla do paměti načtena) a mezivýsledky ukládá zpět do paměti.
Jak bylo zmíněno, na dnešních osobních počítačích (PC) je takovým programem většinou jádro operačního systému. Základními starostmi jádra je inicializace počítače (inicializace nejrůznějšího hardware atp.), správa paměti (přerozdělování paměti mezi procesy, její alokace a uvolňování apod.), řízení a správa procesů (načítání programů do paměti, přerozdělování procesorového času mezi procesy, umožnění meziprocesorové komunikace apod.). A zde se již pomalu začínáme dostávat k tomu, co nás zajímá – paměť našeho procesu.
Program, proces, virtuální paměť
Doposud zde byly používány dva termíny – „program“ a „proces“ – bez jejich vysvětlení. To by se teď mělo napravit. Pokud mluvíme o programu, je tím myšlen zkompilovaný zdrojový kód uložený na nějakém externím zařízení (kupříkladu na pevném disku). Proces je pak již jedna konkrétní instance programu nahraná operačním systémem do paměti. Příklad asi bude lepší. Programem označme např. interpret příkazů systému (shell, příkazová řádka). Takový program je uložen na pevném disku jako binární spustitelný soubor. Když ho pak spustíme, jádro tento soubor namapuje do paměti – vytvoří se jedna jeho instance neboli proces. Pokud spustíme program znovu, jádro ho opět namapuje do paměti (jak, to závisí na struktuře soubor) – vznikne další proces. Z čehož plyne, že program je zpravidla jen jeden, zatímco procesů stejného programu může být více. Bavit se tedy o paměti programu by nebylo zrovna správné, jelikož program jako takový žádnou paměť přístupnou nemá, tu mají přístupnou až jeho konkrétní instance – procesy.
Když teď víme, co je co, můžeme se pustit dál. Každý takový proces tedy má nějakou svou paměť. Jelikož snad všechny dnešní operační systémy umožňují běh více procesů ve stejnou chvíli (tzv. „multitasking“), proces samozřejmě nemůže mít přístup do celé paměti – tedy alespoň ne přímo. Používá se tedy tzv. „virtuální paměti“ procesu. Proces vidí, jako by měl přístup prakticky do celé paměti (minimálně do její velké části), ale jednotlivé virtuální adresy mohou být fyzicky někde naprosto jinde (dokonce ani ne v paměti, ale např. uloženy na disku, ale to v této chvíli až tak důležité není). Lepší popis virtuální paměti, jejího vztahu k fyzické apod. věcí se jistě najde jinde, navíc by to bylo nad rámec toho, oč v garbage collection jde. Velice zjednodušený nákres virtuální paměti by vypadal takto:
Přitom pouze části označené jako „instrukce programu“ a „staticky alokovaná data“ jsou uloženy v našem programu na disku. „Instrukce programu“ mají nejnižší adresu a dno „zásobníku“ má nejvyšší. „Nenamapovaný prostor“ je ta část virtuální paměti, kam by program neměl sahat. A pokud se tak stane, jádru se to většinou nelíbí, takže nám celý proces „sestřelí“. Pak už tu zbývají jen části zvané jako „halda“ a „zásobník“. Nejdříve zásobník. Je to oblast, která je využívána pro volání funkcí, ukládání návratových adres, parametrů funkce a lokálních proměnných. Jak již bylo řečeno, tzv. „dno“ zásobníku má nejvyšší adresu. Adresa vrcholu zásobníku (většinou uložená v procesorovém registru zvaném „stack pointer“) se při každém přidání hodnoty do zásobníku (operace známá jako „PUSH“) zmenšuje – říká se, že zásobník roste dolů. Zásobník je koncipován na ukládání lokálních proměnných, není vhodný pro objemná data a jen těžko se na něj ukládají „rostoucí“ data.
Proto je tu „halda“. Ta právě slouží k ukládání velkých dat a navíc takových, která mají být
přístupné po dobu delší než volání jedné funkce. V jazyce C slouží k alokování a uvolňování paměti na
haldě jistě všem dobře známé funkce malloc()
, resp. free()
. V C++ jsou zde pak operátory new
a delete
. Toto jsou způsoby tzv. „manuální“ správy paměti. Garbage collection je způsob správy automatické. Programátor se nemusí starat o uvolňování paměti, pouze oznámí garbage collectoru, že potřebuje naalokovat paměť dané velikosti, collector paměť naalokuje (má-li dostatek místa samozřejmě) a programátor dostane zpět ukazatel na právě alokovanou paměť, o jejíž uvolňování se nemusí starat – tuto práci již provede collector. A to je celé. Tohle je garbage collection. Není to nic jiného než automatická správa paměti alokované na haldě.
Důvody vzniku
Někteří se možná ptají, k čemu tedy další způsob správy paměti, když tu už jeden je. Manuální spravování paměti na haldě má hned několik problémů. Dokonce tak časté, že i většině z nich se dostalo jejich vlastního pojmenování. Např. všichni jistě někdy slyšeli pojem „memory leak“. Ten označuje paměť, která byla alokována, nebyla dealokována, ale už není nijak dosažitelná (nevede na ni žádný ukazatel). Taková paměť tedy nemůže být použita a ani nejde vrátit zpět systémovému či knihovnímu správci paměti. Můžete si říct, že se přeci tolik neděje. Paměti mají dnešní počítače přeci dost, tak nějaká troška přeci nemůže až tak chybět. Ale když si vezmete program, který pracuje s velmi velkými daty, a tudíž si každá neuvolněná paměť ukousne velkou část paměti procesu, a/nebo dlouho běžící úlohy, jejichž memory leaky mohou být malé, ale třebas již po takovém dni se mohou hezky nasčítat, hned vidíte, že memory leak je chyba, které by se měl programátor vyvarovat.
Dalším jistě známým termínem je tzv. „dangling pointer“. Jde o situaci, kdy alokujeme paměť, její adresu si uložíme a provádíme další činnost (volání dalších funkcí), při které s touto pamětí pracujeme. Může se stát, že paměť při této činnosti uvolníme. Pak, když s ní po provedení činnosti budeme chtít pracovat – použijeme dříve uloženou adresu –, nemusí se stát vůbec nic. Ale taky již tato paměť mohla být znovu alokována jinde v programu a přepsána jinými daty, či dokonce odmapována – nachází se v nenamapovaném prostoru (viz obrázek virtuální paměti).
Nejhorší na těchto chybách je, že jsou jen těžko odhalitelné. Zatímco taková chyba v syntaxi se objeví již při kompilaci, špatná podmínka bude způsobovat nesprávné chování programu, tak tyto druhy chyb se mohou projevovat nepravidelně, a jestli vůbec, a jen v některých případech použití programu. Takže vyvarování se chyb způsobených správou paměti na haldě bylo jistě jedním z důvodů vzniku automatické správy. Člověk není tvor neomylný a počítač dělá pouze a přesně to, co mu člověk poručí. Dalším důvodem by jistě mohla být lenost, kterou člověk oplývá, programátoři zvláště. Proč něco dělat, když to za sebe mohu nechat udělat počítač, ne?
Nevýhody garbage collection
Po předchozím výčtu nejrůznějších nevýhod manuální správy paměti a výhod té automatické by se mohlo zdát, že garbage collection žádné chyby nemá a manuální správa je velkým zdrojem potíží. Ale, jak se říká, nic není černobílé. Samozřejmě, že jsou zde i druhé strany mince pro každou z možností. Největším problémem garbage collection je, že samozřejmě, jako všechno, spotřebovává procesorový čas a navíc, aby bylo možno rozhodovat o tom, je-li objekt v paměti „mrtvý“ (není již odnikud dostupný), nebo živý, musí mít collector o tomto stavu uloženou nějakou informaci, což znamená data, která nejsou nezbytná pro běh programu. První problém by se nemusel zdát až tak hrozný. I podle Moorových zákonů je levnější a jednodušší si počkat pár měsíců, než hardware zase postoupí o kus dál, než platit programátorovi přesčas, aby optimalizoval použité algoritmy. Ale některé garbage collectory mohou způsobovat i dosti znatelné pauzy při běhu programu, což není moc vhodné pro tzv. „real-time“ systémy, kdy každá milisekunda může rozhodovat.
Nevím již, kde jsem na tuto zprávu narazil, ani jestli znáte jazyk zvaný Lua. Jen krátce o tomto jazyku. Je to jazyk, který je určen pro včleňování do aplikací (je tzv. „embeddable“), pro jejich snadnou rozšiřitelnost („pluginovatelnost“). Je distribuován jako malá knihovna napsaná v čistém C, takže je velice jednoduše zkompilovatelný na mnoha platformách. Co je pro nás důležité, je to jazyk, který má vestavěný garbage collector. A jeden tým se právě kvůli jeho jednoduchosti – jak v psaní v něm, tak právě možnosti začlenění – rozhodl použít ho jako řídící jazyk pro jimi konstruovaného robota. Když robota sestavili, napsali program pro jeho řízení a pustili ho do akce, mohli pozorovat, jak se někdy robot přestal řídit naprogramovanými instrukcemi (např. jel dál, i když měl zahnout), pak pokračoval vesele dál. Bylo to způsobeno právě probíhajícím „sběrem“ mrtvých objektů. Takže pro „real-time“ systémy a zařízení toho typu není garbage collection příliš vhodná (alespoň některé algoritmy).
Když známe nevýhody, které nám automatická správa paměti přináší, bude jednoduché odvodit, co je výhodou pro správu manuální. Ano, menší prodlevy (není potřeba provádět sběr mrtvých objektů) a také menší paměťová náročnost (není potřebu si udržovat redundantní informace o alokované paměti).
Co čekat dále
Tento článek by měl být úvodem do miniseriálu do automatické správy paměti zvané „garbage collection“. Zatím jsme probrali, co je program, proces, jak vypadá paměť procesu a shrnuli si výhody a nevýhody spojené s manuální a automatickou správou paměti. K tomu možná důležitějšímu a nejspíš i zábavnějšímu – algoritmům používaným pro garbage collection – se dostaneme v další části/dalších částech tohoto miniseriálu.