2.40. Bellek ve çöp toplama

MicroPython belleği CPython ile aynı şekilde yönetir: her nesne yığında (heap) yaşar ve bir çöp toplayıcı (GC) artık hiçbir şeyin başvurmadığı nesneleri serbest bırakır. Birkaç yüz kilobayt RAM’e sahip bir aygıtta, yığının nasıl kullanıldığına dikkat etmek arada bir gereklidir; masaüstü Python’da ise nadiren gereklidir.

2.40.1. Yığın (heap)

Yığın (heap), çalışma zamanının Python nesnelerine dağıttığı RAM bölgesidir – çoğu OpenMV kamerasında aslında birden fazla fiziksel bellek havuzuna bölünmüştür. Bir Python ifadesi her yeni nesne oluşturduğunda (bir liste, bir dizge, bir dict, bir tuple, küçük bir tam sayı veya bir singleton olmayan herhangi bir şey), onu tutmak için yığından bir bayt bloğu çıkar. GC bir nesnenin ulaşılamaz olduğunu fark ettiğinde, bloğu geldiği serbest havuza geri verir.

gc modülünde bilinmeye değer iki fonksiyon vardır:

  • gc.mem_free() – yığında şu anda yaklaşık serbest bayt sayısı.

  • gc.collect() – çalışma zamanının bir toplama tetiklemesini beklemek yerine hemen bir toplama döngüsü çalıştırır.

import gc

print("before:", gc.mem_free())
big = [0] * 10000
print("after :", gc.mem_free())
del big
gc.collect()
print("freed :", gc.mem_free())

Tam sayılar derlemeye bağlıdır; önemli olan değişimin yönüdür: büyük şeyler ayırmak mem_free‘yi azaltır ve başvuruları bırakıp bir gc.collect yapmak yığını geri verir.

2.40.2. Parçalanma

Serbest havuz büyülü bir şekilde bitişik değildir. Nesneler farklı ömürlerde gelip gittikçe, toplam boyutu hâlâ büyük olsa bile serbest alan giderek daha küçük parçalara ayrılır. En büyük tek serbest parçadan daha büyük bir nesne ayırmak MemoryError ile başarısız olur – teknik olarak yeterli toplam serbest RAM bulunsa bile.

Yığının iki görünümü. Önce: tek büyük bitişik serbest alan. Sonra: ayrılmış bloklarla iç içe geçmiş, hiçbiri tek başına büyük bir tahsis için yeterince büyük olmayan birçok küçük serbest alan.

Aynı toplam serbest RAM, ne kadar parçalanmış olduğuna bağlı olarak büyük bir arabelleği tutabilir (solda) veya tutmayı reddedebilir (sağda).

Parçalanma çoğunlukla farklı boyutlarda arabellekler ayırıp serbest bırakmaya devam eden uzun süre çalışan betiklerde bir endişe kaynağıdır. Tek en etkili önlem, birçok kısa ömürlü tahsisin bunları dağıtma şansı bulmadan önce, uzun ömürlü arabellekleri programın başına yakın bir yerde önceden ayırmaktır.

2.40.3. Önceden ayırma

İki desen yığının düzgün davranmasını sağlar:

  • Her yinelemede yeni bir liste veya bytearray oluşturmak yerine sabit boyutlu arabellekleri bir kez ayırın ve yeniden kullanın.

  • Sabitleri ve arama tablolarını iç döngülerin dışına çekin; böylece bir kez oluşturulurlar.

buf = bytearray(64)        # one allocation, reused below

def fill(value):
    for i in range(len(buf)):
        buf[i] = value

fill(0)
fill(255)

Döngü içinde yeni bir bytearray oluşturan bir sürümle karşılaştırın: her yineleme, GC’nin daha sonra temizlemesi gereken çöp üretir. Önceden ayrılmış sürüm hiç çöp üretmez.

2.40.4. gc.collect ne zaman çağrılır

gc.collect() normalde otomatiktir – tahsisler yeterli serbest bellek bulamadığında çalışma zamanı bunu tetikler. Onu elle çağırmak iki durumda yararlıdır:

  • Büyük bir nesne grubu kapsamdan çıktıktan hemen sonra, bir sonraki tahsisin maliyetini ödemesini beklemek yerine onları hemen serbest bırakmak için.

  • Bilinen bir maksimum serbest bellek miktarına ihtiyaç duyan bir bölümden hemen önce, GC’nin zamana duyarlı bir işlemin ortasında tetiklenmesini önlemek için.

gc.collect çağrılarını her yere serpiştirmek bir programı daha hızlı yapmaz – toplamanın kendisi zaman alır. Onu, planlanmamış bir toplamanın maliyetinin, kasıtlı olarak çalıştırdığınız birinin maliyetinden daha kötü olacağı noktalarda bilinçli olarak kullanın.