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.