2.40. Paměť a uvolňování paměti (garbage collection)¶
MicroPython spravuje paměť stejným způsobem jako CPython: každý objekt žije na haldě (heap) a garbage collector (GC) uvolňuje objekty, na které už nic neodkazuje. Na zařízení s několika sty kilobajty RAM je občas nutné věnovat pozornost tomu, jak se halda využívá; na desktopovém Pythonu to nutné bývá jen zřídka.
2.40.1. Halda¶
Halda je oblast RAM – na většině kamer OpenMV ve skutečnosti rozdělená napříč více než jedním fyzickým paměťovým fondem – kterou běhové prostředí přiděluje objektům Pythonu. Pokaždé, když výraz v Pythonu vytvoří nový objekt (seznam, řetězec, slovník, n-tici, cokoli, co není malé celé číslo nebo singleton), z haldy se vyčlení blok bajtů, který jej pojme. Když GC zjistí, že objekt je nedosažitelný, vrátí blok do volného fondu, ze kterého pocházel.
Za poznání stojí dvě funkce v modulu gc:
gc.mem_free()– přibližný počet volných bajtů na haldě v daném okamžiku.gc.collect()– okamžitě spustí cyklus uvolňování paměti místo čekání, až jej běhové prostředí samo vyvolá.
import gc
print("before:", gc.mem_free())
big = [0] * 10000
print("after :", gc.mem_free())
del big
gc.collect()
print("freed :", gc.mem_free())
Přesná čísla závisí na sestavení; podstatný je směr změny: alokace velkých věcí snižuje mem_free a zahození odkazů spolu s gc.collect haldu vrátí.
2.40.2. Fragmentace¶
Volný fond není zázračně souvislý. Jak objekty vznikají a zanikají s různou životností, volné místo se tříští na stále menší úseky, i když jeho celková velikost je stále velká. Alokace objektu většího než největší jednotlivý volný úsek selže s MemoryError – přestože je technicky vzato dostatek celkové volné RAM.
Stejné celkové množství volné RAM může pojmout velký buffer (vlevo), nebo jej odmítnout (vpravo) podle toho, jak je fragmentované.¶
Fragmentace je převážně starostí u dlouhoběžících skriptů, které neustále alokují a uvolňují buffery různých velikostí. Jediným nejúčinnějším opatřením je předem alokovat dlouho žijící buffery blízko začátku programu, dříve než je stihne rozházet množství krátkodobých alokací.
2.40.3. Předběžná alokace¶
Haldu zkrotí dva postupy:
Alokujte buffery pevné velikosti jednou a znovu je používejte, místo abyste v každé iteraci vytvářeli nový seznam nebo
bytearray.Vytáhněte konstanty a vyhledávací tabulky z vnitřních cyklů, aby se vytvořily jen jednou.
buf = bytearray(64) # one allocation, reused below
def fill(value):
for i in range(len(buf)):
buf[i] = value
fill(0)
fill(255)
Porovnejte to s verzí, která vytváří nové bytearray uvnitř cyklu: každá iterace produkuje odpad, který musí GC později uklidit. Předem alokovaná verze nevytváří žádný odpad.
2.40.4. Kdy volat gc.collect¶
gc.collect() je normálně automatický – běhové prostředí jej spustí, když alokace nemohou najít dostatek volné paměti. Volat jej ručně je užitečné ve dvou situacích:
Hned poté, co velká dávka objektů opustila platnost, aby se uvolnily okamžitě, místo čekání, až cenu zaplatí příští alokace.
Těsně před částí, která potřebuje známé maximální množství volné paměti, aby se zabránilo spuštění GC uprostřed časově citlivé operace.
Rozsypávání volání gc.collect všude program nezrychlí – samotné uvolňování zabere čas. Používejte jej uvážlivě, na místech, kde by cena neplánovaného uvolnění byla horší než cena toho, které jste spustili záměrně.