2.40. Memorija i sakupljanje smeća¶
MicroPython upravlja memorijom na isti način kao CPython: svaki objekt živi na heapu, a sakupljač smeća (GC) oslobađa objekte koje više ništa ne referencira. Na uređaju s nekoliko stotina kilobajta RAM-a, obraćanje pažnje na to kako se heap koristi povremeno je nužno; na stolnom Pythonu to rijetko jest.
2.40.1. Heap¶
Heap je područje RAM-a – na većini OpenMV kamera zapravo podijeljeno na više od jednog fizičkog memorijskog spremišta – koje radno okruženje dodjeljuje Python objektima. Svaki put kada Python izraz stvori novi objekt (popis, niz, rječnik, n-torku, bilo što što nije mali cijeli broj ili singleton), blok bajtova izlazi iz heapa da bi ga sadržao. Kada GC primijeti da je objekt nedostupan, vraća blok u slobodno spremište iz kojeg je došao.
Vrijedi poznavati dvije funkcije u modulu gc:
gc.mem_free()– približan broj slobodnih bajtova na heapu upravo sada.gc.collect()– pokrenite ciklus sakupljanja odmah umjesto da čekate da ga radno okruženje aktivira.
import gc
print("before:", gc.mem_free())
big = [0] * 10000
print("after :", gc.mem_free())
del big
gc.collect()
print("freed :", gc.mem_free())
Točni brojevi ovise o verziji; smjer promjene je ono što je važno: alociranje velikih stvari smanjuje mem_free, a ispuštanje referenci uz gc.collect vraća heap.
2.40.2. Fragmentacija¶
Slobodno spremište nije čarobno neprekinuto. Kako objekti dolaze i odlaze s različitim vijekom trajanja, slobodni prostor se lomi na sve manje i manje komade čak i kada je njegova ukupna veličina i dalje velika. Alociranje objekta većeg od najvećeg pojedinačnog slobodnog komada ne uspijeva uz MemoryError – iako tehnički postoji dovoljno ukupnog slobodnog RAM-a.
Isti ukupni slobodni RAM može primiti veliki međuspremnik (lijevo) ili to odbiti (desno) ovisno o tome koliko je fragmentiran.¶
Fragmentacija je uglavnom briga kod dugotrajnih skripti koje neprestano alociraju i oslobađaju međuspremnike različitih veličina. Najučinkovitija mjera ublažavanja jest unaprijed alocirati dugovječne međuspremnike blizu početka programa, prije nego što je mnoštvo kratkovječnih alokacija imalo priliku raspršiti ih.
2.40.3. Unaprijedna alokacija¶
Dva obrasca čine da se heap dobro ponaša:
Alocirajte međuspremnike fiksne veličine jednom i ponovno ih koristite, umjesto da gradite novi popis ili
bytearraypo iteraciji.Izvucite konstante i tablice za pretraživanje iz unutarnjih petlji tako da se stvore jednom.
buf = bytearray(64) # one allocation, reused below
def fill(value):
for i in range(len(buf)):
buf[i] = value
fill(0)
fill(255)
Usporedite s verzijom koja stvara novi bytearray unutar petlje: svaka iteracija proizvodi smeće koje GC kasnije mora počistiti. Unaprijed alocirana verzija ne stvara nikakvo smeće.
2.40.4. Kada pozvati gc.collect¶
gc.collect() normalno je automatska – radno okruženje je aktivira kada alokacije ne mogu pronaći dovoljno slobodne memorije. Ručno pozivanje korisno je u dvije situacije:
Odmah nakon što je velika serija objekata izašla iz dosega, da bi se oslobodili odmah umjesto da se čeka da sljedeća alokacija plati tu cijenu.
Neposredno prije dijela koji treba poznatu maksimalnu količinu slobodne memorije, kako bi se izbjeglo aktiviranje GC-a usred vremenski osjetljive operacije.
Posipanje poziva gc.collect posvuda ne čini program bržim – samo sakupljanje oduzima vrijeme. Koristite ga promišljeno, na točkama gdje bi cijena neplaniranog sakupljanja bila gora od cijene onog koje ste pokrenuli namjerno.