A .NET környezet esetén a memória kezeléséért és a nem használt változók területének felszabadításáért a szemétgyűjtő felel.1
A szemétgyűjtő több feladatot is ellát. A nevéből adódó legfontosabb feladata a már nem használt változók által foglalt terület felszabadítása és újra kioszthatóvá tétele. Azonban ez csak a feladat egy része, a másik rész a töredezettségmentesítés.
A korábban említett referencia típusok a heap-en tárolódnak. A heap a számítógépek memóriájának2 egy olyan szegmense, amit egy kupacként kezelünk, mivel ebből van több. Egy másik szegmens a verem ami metódus híváskor értékek átadására szolgál.
Minden egyes alkalommal, amikor egy referencia típust létrehozunk, akkor a keretrendszer foglal számára elegendő helyet a heap-en, majd pedig mikor semmi referencia nem mutat rá, akkor majd a szemét gyűjtő felszabadítja.
Ennyi felvezetés után nézzünk egy példát, hogy miért szükséges a töredezettségmentesítés. Tételezzük fel, hogy a heap mérete, a példa egyszerűsége miatt 128 byte. Ezen a 128 byte memórián 3 változó osztozik a következő arányban:
var1: 30 byte
var2: 10 byte
var3: 50 byte
Ez összesen 90 byte memória, vagyis van szabadon 38 byte memória. Tételezzük fel, hogy a 2. változó által foglalt 10 byte megüresedik, felszabadul, vagyis elméletben van 48 byte szabad memória. Azonban ha egy 40 byte méretű változónak szeretnénk helyet foglalni, akkor hibába ütköznénk, mivel csupán 38 byte van egybefüggően. Itt jön képbe a töredezettségmentesítés.
.NET esetén ilyenkor a szituációt érzékelve a szemétgyűjtő áthelyezi a 3. változót közvetlenül az első után, a megüresedett helyre. Így lesz egybefüggően hely az új változónak.
Ezen műveletsor nem minden esetben játszódik le, mivel ez egy igen CPU idő igényes műveletsor is lehet, ha több száz változót kell újra allokálni. Tehát amíg van elég memória, addig nem foglalkozik a töredezettségmentesítéssel.
A .NET keretrendszer az úgynevezett generációs elvű szemétgyűjtést alkalmazza. Ez azt jelenti, hogy a létrehozott objektumokat a keretrendszer generációkba sorolja az életciklusuk során.
-
Generáció
A legújabban létrehozott objektumok (lokális változók), tipikusan rövid életű változók tárolására szolgál. A rövid életű változók tipikusan az átmeneti változók, illetve azok amelyek csak egy metóduson belül léteznek.
-
Generáció
Ez a generáció átmenetet képez a hosszú és rövid életciklusú objektumok között.
-
Generáció
Ebbe a generációba a hosszú életciklusú változók kerülnek, tipikusan ilyenek azok a változók amelyek a program futásának teljes életciklusa alatt megmaradnak és a statikus változók.
A szemétgyűjtés nem minden esetben érinti minden generációt. Leggyakrabban 0. Generáción fog megtörténni. Ha a 2. Generáció is érintett, akkor teljes szemétgyűjtésről beszélünk. Tipikusan ilyenkor történik meg a töredezettségmentesítés.
Azon objektumok amelyek túlélik a szemétgyűjtést azok a következő generációba kerülnek, kivéve ha már a 2. generációban vannak, mert akkor ott maradnak.
Amennyiben a szemétgyűjtő érzékeli, hogy egy adott generációban túl magas a túlélő objektumok száma, akkor növeli az adott generációhoz tartozó kiosztható memóriaterületet. Mivel a 0. és 1. generáció átmeneti generációnak számít az ezekben elhelyezkedő objektumok külön memória szegmensben (Ephemeral, magyarul múlandó) helyezkednek el. Ezen szegmens mérete változik az operációs rendszertől függően. 32 bites kliensek esetén ez 16MiB, míg 64 bites kliensek esetén 256MiB.
Ez a fajta viselkedés segít egyensúlyt teremteni két feltétel között: ne tartson sokáig és ne legyen túl nagy a memória használata az alkalmazásunknak. Több magos processzorok esetén a keretrendszer a gyűjtést tipikusan egy külön szálon igyekszik végrehajtani a .NET 4 óta.
-
Ez az állítás minden menedzselt nyelvre és környezetre igaz. Eltérés a megvalósításban és a működésben van, de az alap koncepció azonos.↩
-
Többfeladatos operációs rendszerek esetében (szinte az összes) minden folyamat külön virtuális heap és stack területet kap, hogy a programok ne tudják egymást felülírni és átírni.↩