Bevezetés

A memóriakezelés a memória hatékony kiosztásának, felszabadításának és koordinálásának folyamata, hogy a különböző folyamatok zökkenőmentesen működjenek, és optimálisan hozzáférjenek a különböző rendszerforrásokhoz. A memóriakezelés magában foglalja a memória megtisztítását is azoktól az objektumoktól, amelyekhez már nem férnek hozzá.

A Pythonban a memóriakezelő felelős az ilyen jellegű feladatokért azáltal, hogy periodikusan lefut, hogy megtisztítsa, kiossza és kezelje a memóriát. A C, a Java és más programozási nyelvektől eltérően a Python az objektumokat referenciaszámolással kezeli. Ez azt jelenti, hogy a memóriakezelő nyomon követi a programban az egyes objektumokra való hivatkozások számát. Amikor egy objektum hivatkozások száma nullára csökken, ami azt jelenti, hogy az objektumot már nem használják, a szemétgyűjtő (a memóriakezelő része) automatikusan felszabadítja az adott objektum memóriáját.

A felhasználónak nem kell aggódnia a memóriakezelés miatt, mivel a memória ki- és felszabadításának folyamata teljesen automatikus. A visszakapott memóriát más objektumok használhatják.

Python Garbage Collection

Amint azt már korábban kifejtettük, a Python törli azokat az objektumokat, amelyekre a programban már nem hivatkoznak, hogy memóriateret szabadítson fel. Ezt a folyamatot, amelynek során a Python felszabadítja a már nem használt memóriablokkokat, szemétgyűjtésnek nevezzük. A Python Garbage Collector (GC) a program végrehajtása közben fut, és akkor lép működésbe, ha a hivatkozások száma nullára csökken. A hivatkozások száma növekszik, ha egy objektumhoz új nevet rendelünk, vagy ha egy tárolóba, például tuple-ba vagy szótárba helyezzük. Hasonlóképpen, a referenciaszám csökken, ha egy objektumra való hivatkozást újra hozzárendelik, ha az objektum hivatkozása kikerül a hatókörből, vagy ha egy objektumot törölnek.

A memória egy halom, amely a programban használt objektumokat és más adatstruktúrákat tartalmaz. Ennek a halomterületnek a ki- és felosztását a Python memóriakezelője vezérli az API függvények segítségével.

Python objektumok a memóriában

A Pythonban minden változó objektumként viselkedik. Az objektumok lehetnek egyszerűek (számokat, karakterláncokat stb. tartalmazó) vagy konténerek (szótárak, listák vagy felhasználó által definiált osztályok). Továbbá a Python egy dinamikusan tipizált nyelv, ami azt jelenti, hogy nem kell deklarálnunk a változókat vagy azok típusát, mielőtt használnánk őket a programban.

Példa:

Ha megnézzük a fenti program első két sorát, az x objektumot ismerjük. Amikor töröljük az x objektumot, és megpróbáljuk használni, hibát kapunk, miszerint a x változó nincs definiálva.

Láthatjuk, hogy a Pythonban a szemétgyűjtés teljesen automatizált, és a programozónak nem kell aggódnia emiatt, ellentétben az olyan nyelvekkel, mint a C.

A szemétgyűjtő módosítása

A Python szemétgyűjtőnek három generációja van, amelyekbe az objektumokat osztályozzák. Egy új objektum az életciklusa kezdetén a szemétgyűjtő első generációja. Ahogy az objektum túléli a szemétgyűjtést, úgy kerül feljebb a következő generációkba. A szemétgyűjtő mindhárom generációjának van egy-egy küszöbértéke. Pontosabban, amikor az allokációk számának mínusz a de0allokációk számának küszöbértéke túllépésre kerül, az adott generáció lefuttatja a szemétgyűjtést.

A korábbi generációk is gyakrabban kerülnek szemétgyűjtésre, mint a magasabb generációk. Ennek oka, hogy az újabb objektumok nagyobb valószínűséggel kerülnek selejtezésre, mint a régi objektumok.

A gc modul tartalmaz függvényeket a küszöbérték módosítására, a szemétgyűjtési folyamat manuális elindítására, a szemétgyűjtési folyamat letiltására stb. A get_threshold() módszerrel ellenőrizhetjük a szemétgyűjtő különböző generációinak küszöbértékeit:

import gcprint(gc.get_threshold())

Mintakimenet:

(700, 10, 10)

Amint látjuk, itt az első generáció küszöbértéke 700, a másik két generációé pedig 10 érték.

A gc modul set_threshold() metódusával megváltoztathatjuk a szemétgyűjtési folyamat elindításának küszöbértékét:

gc.set_threshold(900, 15, 15)

A fenti példában mind a 3 generáció esetében megnöveltük a küszöbértéket. A küszöbérték növelése csökkenti a szemétgyűjtő futtatásának gyakoriságát. Normális esetben fejlesztőként nem kell túl sokat gondolkodnunk a Python szemétgyűjtésről, de ez hasznos lehet, amikor a Python futási idejét a célrendszerre optimalizáljuk. Az egyik legfontosabb előnye, hogy a Python szemétgyűjtési mechanizmusa sok alacsony szintű részletet automatikusan kezel a fejlesztő számára.

Miért végezzük a kézi szemétgyűjtést?

Tudjuk, hogy a Python-értelmező nyomon követi a programban használt objektumokra való hivatkozásokat. A Python korábbi verzióiban (az 1.6-os verzióig) a Python-értelmező csak a referenciaszámláló mechanizmust használta a memória kezelésére. Amikor a referenciaszám nullára csökken, a Python-értelmező automatikusan felszabadítja a memóriát. Ez a klasszikus referenciaszámláló mechanizmus nagyon hatékony, kivéve, hogy nem működik, ha a programban referenciaciklusok vannak. Hivatkozási ciklus akkor keletkezik, ha egy vagy több objektum egymásra hivatkozik, és ezért a hivatkozások száma soha nem éri el a nullát.

Nézzünk egy példát.

>>> def create_cycle():... list = ... list.append(list)... return list... >>> create_cycle()]

A fenti kód létrehoz egy hivatkozási ciklust, amelyben az list objektum önmagára hivatkozik. Ezért a list objektum memóriája nem szabadul fel automatikusan, amikor a függvény visszatér. A referenciaciklus-probléma nem oldható meg a referenciaszámolással. Ez a referenciaciklus-probléma azonban megoldható a Python-alkalmazásunkban a szemétgyűjtő viselkedésének megváltoztatásával.

Ezért használhatjuk a gc modul gc.collect() függvényét.

import gcn = gc.collect()print("Number of unreachable objects collected by GC:", n)

A gc.collect() visszaadja az általa összegyűjtött és felszabadított objektumok számát.

A kézi szemétgyűjtés kétféleképpen végezhető: időalapú vagy eseményalapú szemétgyűjtés.

Az időalapú szemétgyűjtés elég egyszerű: a gc.collect() függvényt egy meghatározott időintervallum után hívjuk meg.

Az eseményalapú szemétgyűjtés egy esemény bekövetkezése után hívja meg a gc.collect() függvényt (azaz amikor az alkalmazásból kilépünk, vagy az alkalmazás egy meghatározott ideig üresen marad).

Megértjük a manuális szemétgyűjtés működését néhány hivatkozási ciklus létrehozásával.

A kimenet az alábbi:

Creating garbage...Collecting...Number of unreachable objects collected by GC: 8Uncollectable garbage: 

A fenti szkript létrehoz egy listaobjektumot, amelyre egy változó hivatkozik, amelynek kreatív neve list. A listaobjektum első eleme önmagára hivatkozik. A listaobjektum hivatkozási száma mindig nagyobb, mint nulla, még akkor is, ha a programban törölték vagy hatókörön kívül van. Ezért a list objektum a körkörös hivatkozás miatt nem kerül szemétgyűjtésre. A Pythonban a szemétgyűjtő mechanizmus automatikusan ellenőrzi és rendszeresen begyűjti a körkörös hivatkozásokat.

A fenti kódban, mivel a hivatkozások száma legalább 1, és soha nem érheti el a 0-t, a gc.collect() meghívásával kényszerítettük az objektumok szemétgyűjtését. Ne feledjük azonban, hogy ne kényszerítsük ki gyakran a szemétgyűjtést. Ennek oka, hogy a GC-nek még a memória felszabadítása után is időbe telik, amíg értékeli az objektum szemétgyűjtésre való alkalmasságát, ami processzoridőt és erőforrásokat vesz igénybe. Ne feledje továbbá, hogy a szemétgyűjtőt csak az alkalmazás teljes elindulása után kezelje manuálisan.

Következtetés

Ebben a cikkben azt tárgyaltuk, hogy a Pythonban a memóriakezelést hogyan kezeljük automatikusan a referenciaszámlálás és a szemétgyűjtési stratégiák segítségével. Szemétgyűjtés nélkül a sikeres memóriakezelési mechanizmus megvalósítása Pythonban lehetetlen. Emellett a programozóknak nem kell aggódniuk a kiosztott memória törlése miatt sem, mivel erről a Python memóriakezelője gondoskodik. Ez kevesebb memóriaszivárgást és jobb teljesítményt eredményez.

Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.