Az unmanaged nyelveken történő fejlesztés során az egyik legidegőrlőbb hiba, amibe belefuthatunk, a memóriafolyás: az objektumaink számára lefoglalt memóriát elfelejtjük felszabadítani, így az csak fogy, fogy, fogy - aztán pedig fagy.
Egy .net-ben készült alkalmazásnál a futtatókörnyezet leveszi a vállunkról a takarítás terhét - nekünk csak arra kell ügyelnünk, hogy objektumainkat példányosítjuk, a már nem használt obektumok által használt memóriát a szemétgyűjtő (Garbage Collector, GC) pedig automatikusan felszabadítja.
Ez persze nincs ingyen, a szemétgyűjtés erőforrásigényes dolog, a GC-nek végig kell túrnia a heap-et elérhetetlen objektumok után kutatva. Annak érdekében, hogy ez minnél kevesebb erőforrást vegyen el a futó alkalmazásoktól, a szemétgyűjtő generációkba sorolja az objektumpéldányainkat.
Összesen három generáció létezik: 0., 1. és 2. generációs objektumokról beszélhetünk. Az, hogy egy objektumpéldány hányadik generációs, attól függ, hogy hány szemétgyűjtést élt túl.
Amikor eljön az idő - pl. fogy a szabad memória, vagy egy GC.Collect()-tel kierőszakoljuk -, és a szemétgyűjtő futni kezd, először a 0. generációs objektumokat veszi célba, magyarán azokat, amik a legutóbbi garbage collection óta lettek létrehozva. Amikre nincs élő referencia (leszámítva a WeakReference-eket), azokat kipucolja a memóriából.
Ha a GC úgy gondolja, hogy még mindig nem lesz elég a szabad memóriából, akkor jöhetnek az első generációs objektumok, ha pedig még mindig gáz van, akkor a második generációsok. Ha még akkor is gáz van, abból előbb-utóbb egy jól fejlett OutOfMemoryException lesz. Ha nem ennyire kritikus a helyzet, akkor a GC fellélegezhet, a túlélő objektumpéldányokkal egyetemben ("Megint örgebbek lettünk egy generációval!").
A generációkba sorolás mögött az áll, hogy faragni lehessen kicsit a szemétgyűjtés erőforrás-igényéből. A mesebeli "átlagos program" ugyanis sok "temporális" objektumot hoz létre - gondoljuk itt pl. a metódusokban létrehozott, vissza nem adott példányokra, vagy akár egy { } blokk belsejében élő, a blokkot elhagyva elérhetetlenné váló objektumokra. Ezek (mint minden más objektum is), létrehozásukkor 0. generációsak lesznek, és jó eséllyel ki lesznek takarítva az első szemétgyűjtés alkalmával.
Viszont azok a példányok, amik valami miatt sokáig élnek - ilyenjei is vannak az "átlagos programnak", pl. singletonok -, gyorsan (egész pontosan két szemétgyűjtési ciklus alatt) a nagy öregek asztalához kerülnek, azokat pedig igyekszik csak ritkán, végszükség esetén zargatni a GC.
A GC.Collect()-nek egyébként van olyan overloadja, amiben azt is megmondhatjuk a szemétgyűjtőnek, hogy most igenis nézzen körül a korosabb objektumok között is - de ez inkább érdekesség, mint a mindennapi fejlesztésnél hasznos gyakorlat. A GC teszi a dolgát, a legtöbbször nem nyerünk vele semmit, ha okosabbak próbálunk lenni nála. Egyes, a memória-kezelésért felelős osztályoknál (pl. a System.Runtime.MemoryFailPoint-nál) egyenest azt mondja az MSDN: "[Note]: This class is intended for use in advanced development.".
Pedig milyen érdekes.

0 megjegyzés:
Megjegyzés küldése