Esittely
Muistinhallinta on prosessi, jossa muistia varataan, poistetaan ja koordinoidaan tehokkaasti niin, että kaikki eri prosessit toimivat sujuvasti ja pääsevät optimaalisesti käsiksi järjestelmän eri resursseihin. Muistinhallintaan kuuluu myös muistin puhdistaminen objekteista, joita ei enää käytetä.
Pythonissa muistinhallinta vastaa tällaisista tehtävistä ajoittain suorittamalla muistin siivoamisen, varaamisen ja hallinnan. Toisin kuin C:ssä, Javassa ja muissa ohjelmointikielissä, Pythonissa objekteja hallitaan viittauslaskennan avulla. Tämä tarkoittaa sitä, että muistinhallinta pitää kirjaa kunkin ohjelmassa olevan objektin viittausten määrästä. Kun objektin viittausluku laskee nollaan, mikä tarkoittaa, että objektia ei enää käytetä, roskienkerääjä (osa muistinhallintaa) vapauttaa automaattisesti kyseisen objektin muistin.
Käyttäjän ei tarvitse huolehtia muistinhallinnasta, sillä muistin allokointi- ja poistoprosessi on täysin automaattinen. Vapautunut muisti voidaan käyttää muiden objektien käyttöön.
Python Garbage Collection
Kuten aiemmin selitettiin, Python poistaa muistitilan vapauttamiseksi objekteja, joihin ohjelmassa ei enää viitata. Tätä prosessia, jossa Python vapauttaa muistilohkoja, joita ei enää käytetä, kutsutaan roskienkeruuksi. Pythonin roskienkerääjä (GC) toimii ohjelman suorituksen aikana ja käynnistyy, jos viittausten määrä vähenee nollaan. Viitteiden määrä kasvaa, jos objektille annetaan uusi nimi tai se sijoitetaan säiliöön, kuten tupleen tai sanakirjaan. Vastaavasti viittausluku pienenee, kun viittaus objektiin siirretään uudelleen, kun objektin viittaus menee soveltamisalan ulkopuolelle tai kun objekti poistetaan.
Muisti on kasa, joka sisältää objekteja ja muita ohjelmassa käytettäviä tietorakenteita. Tämän kasan tilan varaamista ja poistamista ohjaa Pythonin muistinhallinta API-funktioiden avulla.
Pythonin objektit muistissa
Jokainen muuttuja Pythonissa toimii objektina. Oliot voivat olla joko yksinkertaisia (jotka sisältävät numeroita, merkkijonoja jne.) tai säiliöitä (sanakirjoja, luetteloita tai käyttäjän määrittelemiä luokkia). Lisäksi Python on dynaamisesti tyypitetty kieli, mikä tarkoittaa, että meidän ei tarvitse ilmoittaa muuttujia tai niiden tyyppejä ennen kuin käytämme niitä ohjelmassa.
Esimerkiksi:
Jos katsot yllä olevan ohjelman kahta ensimmäistä riviä, tunnetaan objekti x
. Kun poistamme objektin x
ja yritämme käyttää sitä, saamme virheilmoituksen, jonka mukaan muuttujaa x
ei ole määritelty.
Voit huomata, että roskienkeruu Pythonissa on täysin automatisoitu eikä ohjelmoijan tarvitse huolehtia siitä, toisin kuin esimerkiksi C-kielissä.
Modifiointi roskienkerääjään
Pythonin roskienkerääjällä on kolme sukupolvea, joihin objektit luokitellaan. Uusi objekti elinkaarensa alkupäässä on roskienkerääjän ensimmäinen sukupolvi. Kun objekti selviää roskienkeräyksestä, se siirtyy seuraaviin sukupolviin. Jokaisella roskienkerääjän kolmella sukupolvella on kynnysarvo. Tarkemmin sanottuna, kun allokointien määrän miinus de0allokointien määrän kynnysarvo ylittyy, kyseinen sukupolvi suorittaa roskienkeräyksen.
Aikaisemmat sukupolvet myös kerätään roskiin useammin kuin ylemmät sukupolvet. Tämä johtuu siitä, että uudemmat objektit hävitetään todennäköisemmin kuin vanhat objektit.
Moduuli gc
sisältää funktioita, joilla voidaan muuttaa kynnysarvoa, käynnistää roskienkeräysprosessi manuaalisesti, poistaa roskienkeräysprosessi käytöstä jne. Voimme tarkistaa roskienkerääjän eri sukupolvien kynnysarvot käyttämällä get_threshold()
-metodia:
import gcprint(gc.get_threshold())
Esimerkkitulos:
(700, 10, 10)
Kuten näet, tässä meillä on kynnysarvo 700 ensimmäiselle sukupolvelle ja 10 kummallekin kahdelle muulle sukupolvelle.
Voidaan muuttaa kynnysarvoa roskienkeräysprosessin käynnistämiseksi gc
-moduulin set_threshold()
-metodilla:
gc.set_threshold(900, 15, 15)
Yllä olevassa esimerkissä olemme nostaneet kynnysarvoa kaikille kolmelle sukupolvelle. Kynnysarvon kasvattaminen vähentää roskienkerääjän suoritustiheyttä. Normaalisti meidän ei tarvitse kehittäjänä miettiä liikaa Pythonin roskienkeräystä, mutta tämä voi olla hyödyllistä, kun optimoimme Pythonin suoritusaikaa kohdejärjestelmääsi varten. Yksi tärkeimmistä eduista on se, että Pythonin roskienkeräysmekanismi hoitaa paljon matalan tason yksityiskohtia kehittäjän puolesta automaattisesti.
Miksi suorittaa manuaalinen roskienkeräys?
Tiedämme, että Python-tulkki pitää kirjaa ohjelmassa käytettyjen objektien viittauksista. Aikaisemmissa Python-versioissa (versioon 1.6 asti) Python-tulkki käytti muistin käsittelyyn vain viitteiden laskentamekanismia. Kun viitteiden määrä laskee nollaan, Python-tulkki vapauttaa muistin automaattisesti. Tämä klassinen viitteiden laskentamekanismi on erittäin tehokas, paitsi että se ei toimi, kun ohjelmassa on viittaussyklejä. Viittaussykli syntyy, jos yksi tai useampi objekti viittaa toisiinsa, eikä viittausluku näin ollen koskaan laske nollaan.
Katsotaanpa esimerkki.
>>> def create_cycle():... list = ... list.append(list)... return list... >>> create_cycle()]
Yllä oleva koodi luo viittaussyklin, jossa objekti list
viittaa itseensä. Näin ollen objektin list
muistia ei vapauteta automaattisesti, kun funktio palaa. Viittaussykliongelmaa ei voida ratkaista viittauslaskennalla. Viittaussykliongelma voidaan kuitenkin ratkaista muuttamalla roskienkerääjän käyttäytymistä Python-sovelluksessasi.
Tässä tarkoituksessa voimme käyttää gc
-moduulin gc.collect()
-funktiota.
import gcn = gc.collect()print("Number of unreachable objects collected by GC:", n)
Funktio gc.collect()
palauttaa kerättyjen ja vapautettujen objektien määrän.
Manuaalisen roskienkeräyksen voi suorittaa kahdella tavalla: aikapohjaisella tai tapahtumapohjaisella roskienkeräyksellä.
Aikapohjainen roskienkeräys on melko yksinkertaista: gc.collect()
-funktiota kutsutaan kiinteän ajan kuluttua.
Tapahtumapohjainen roskienkeruu kutsuu gc.collect()
-funktiota tapahtuman jälkeen (eli kun sovellus lopetetaan tai sovellus pysyy käyttämättömänä tietyn ajanjakson ajan).
Ymmärretäänpä manuaalisen roskienkeruun toimintaa luomalla muutama referenssisykli.
Tulos on seuraava:
Creating garbage...Collecting...Number of unreachable objects collected by GC: 8Uncollectable garbage:
Ylläoleva komentosarjakuvaus luo lista-objektin, johon viitataan muuttujan avulla, joka on nimetty luontevasti nimellä list
. Listaobjektin ensimmäinen elementti viittaa itseensä. Luettelo-objektin viittausluku on aina suurempi kuin nolla, vaikka se poistettaisiinkin ohjelmassa tai se olisi poissa toimialueelta. Näin ollen list
-objektia ei kerätä roskiin kiertoviittauksen vuoksi. Pythonin roskienkeräysmekanismi tarkistaa ja kerää ympyräviittaukset automaattisesti määräajoin.
Yllä olevassa koodissa, koska viittausluku on vähintään 1 eikä voi koskaan saavuttaa nollaa, olemme keränneet objektit väkisin roskiin kutsumalla gc.collect()
. Muista kuitenkin, ettet pakota roskienkeruuta usein. Syynä on se, että muistin vapauttamisen jälkeenkin GC:llä kestää aikaa arvioida objektin kelpoisuus roskienkeräykseen, mikä vie prosessoriaikaa ja resursseja. Muista myös hallita roskienkerääjää manuaalisesti vasta sen jälkeen, kun sovelluksesi on käynnistynyt kokonaan.
Tulos
Tässä artikkelissa käsiteltiin, miten muistinhallinta Pythonissa hoidetaan automaattisesti käyttämällä viittauslaskenta- ja roskienkeräysstrategioita. Ilman roskienkeräystä onnistuneen muistinhallintamekanismin toteuttaminen Pythonissa on mahdotonta. Ohjelmoijan ei myöskään tarvitse huolehtia allokoidun muistin poistamisesta, sillä siitä huolehtii Pythonin muistinhallinta. Tämä vähentää muistivuotoja ja parantaa suorituskykyä.