Bellek Yönetimi¶
C/C++ gibi programlama dillerinin aksine, MicroPython otomatik bellek yönetimini destekleyerek bellek yönetimi ayrıntılarını geliştiriciden gizler. Otomatik bellek yönetimi, işletim sistemleri veya uygulamalar tarafından belleğin ayrılmasını ve serbest bırakılmasını otomatik olarak yönetmek için kullanılan bir tekniktir. Bu, bir nesneye ayrılan belleği serbest bırakmayı unutmak gibi zorlukları ortadan kaldırır. Otomatik bellek yönetimi ayrıca zaten serbest bırakılmış belleği kullanma gibi kritik bir sorunu da önler. Otomatik bellek yönetiminin birçok biçimi vardır; bunlardan biri çöp toplamadır (garbage collection, GC).
Çöp toplayıcının genellikle iki sorumluluğu vardır;
Yeni nesneleri kullanılabilir bellekte ayırmak.
Kullanılmayan belleği serbest bırakmak.
Birçok GC algoritması vardır, ancak MicroPython belleği yönetmek için İşaretle ve Süpür (Mark and Sweep) ilkesini kullanır. Bu algoritmanın, yığını (heap) tarayarak tüm canlı nesneleri işaretleyen bir işaretleme aşaması, işaretlenmemiş tüm nesneleri geri kazanan bir süpürme aşaması vardır.
MicroPython’daki çöp toplama işlevselliği gc yerleşik modülü aracılığıyla kullanılabilir:
>>> x = 5
>>> x
5
>>> import gc
>>> gc.enable()
>>> gc.mem_alloc()
1312
>>> gc.mem_free()
2071392
>>> gc.collect()
19
>>> gc.disable()
>>>
gc.disable() çağrıldığında bile, toplama işlemi gc.collect() ile tetiklenebilir.
C Kodundan MicroPython Belleği¶
“Python yığınından” (heap) bellek ayıran C kodu yazarken çöp toplayıcının farkında olmak gerekir (yani m_malloc(), m_malloc0(), m_free() vb. işlevler).
Çöp toplayıcının işaretleme aşaması, aşağıdaki köklerden başlayarak yığın belleğine giden canlı işaretçileri tarar:
Ana Python çalışma zamanının (veya REPL’in) yığını (stack).
Python iş parçacıklarını yerel işletim sistemi iş parçacıklarının veya görevlerinin üzerine uygulayan portlar için her bir “Python iş parçacığının” yığını (stack).
C kodunda
MP_REGISTER_ROOT_POINTERmakrosu kullanılarak tanımlanan “kök işaretçiler”. Bunlar, Python yığınına statik kapsamlı işaretçilere sahip olmanın önerilen yoludur.m_tracked_calloc(),m_tracked_reallocvem_tracked_free()işlevleriyle yapılan izlenen ayırmalar. Bu özel işlevler, çöp toplayıcı tarafından her zaman canlı olarak kabul edilen bir bellek bloğu ayırmaya olanak tanır. C’deki bellek ayırmaya benzer şekilde, bu bellek yalnızcam_tracked_free()çağrılarak veya yazılımsal sıfırlama (soft reset) ile serbest bırakılır. Her izlenen ayırmanın küçük bir bellek kullanımı ve çalışma zamanı ek yükü vardır. Bu özellik tüm portlarda varsayılan olarak etkin değildir.
Çöp toplayıcı daha sonra, tüm adresler tükenene kadar kök işaretçilerin işaret ettiği tüm belleği özyinelemeli olarak tarar ve işaretler. Bu, MicroPython çalışma zamanı tarafından hâlâ kullanılmakta olan tüm Python nesnelerini bulmak için yeterlidir.
Ancak, aşağıdaki bellek çöp toplayıcı tarafından taranmayacak ve erkenden serbest bırakılabilir:
Yığın belleğine işaretçiler içeren statik veya genel (global) C değişkenleri.
Ayrılmış bir arabelleğin “başına” (yani
m_malloc()tarafından döndürülen tam adrese) değil, bunun yerine ayrılmış arabelleğin içindeki bir adrese (örneğin, iç içe geçmiş bir yapıya işaret eden bir işaretçi) işaret eden işaretçiler. Performans nedeniyle, çöp toplayıcı bu durumlarda kapsayıcı arabelleği işaretlemez.Python kodu çalıştırmayan veya manuel olarak “Python iş parçacığı” olarak kaydedilmemiş herhangi bir iş parçacığının veya RTOS görevinin yığını (stack) (yerel iş parçacıklarını veya görevleri destekleyen portlar için).
Bu senaryolarda serbest bıraktıktan sonra kullanımı (use-after-free) önlemenin yolları:
İzlenen ayırma API’sini kullanın:
m_tracked_calloc(),m_tracked_realloc()vem_tracked_free().Bir işaretçiyi statik bir değişkende saklamak yerine bir kök işaretçi kaydedin (yukarıya bakın).
Kodu yeniden yapılandırın; örneğin, ilgili tüm işaretçileri statik değişkenlerde tutmak yerine, bunları tutan bir tekil (singleton) Python nesnesini (C’de uygulanmış) Python kodunun başlattığı bir API kullanarak.
Not
Yazılımsal Sıfırlama her zaman Python yığınını temizler ve tüm belleği serbest bırakır. Yazılımsal sıfırlamadan sonra yığına işaret eden herhangi bir işaretçi tutmamak önemlidir, çünkü bunlar serbest bırakılmış belleğe işaret eden sarkan (dangling) işaretçiler hâline gelir.
Bazı portlar bir “C yığını” da destekler (bkz. C Dinamik Bellek Ayırma); bu durumda standart C işlevleri malloc vb. çağırarak yazılımsal sıfırlama boyunca geçerli kalacak bellek ayırabilirsiniz.
Nesne modeli¶
Tüm MicroPython nesnelerine mp_obj_t veri tipi ile başvurulur. Bu genellikle sözcük boyutundadır (yani hedef mimarideki bir işaretçi ile aynı boyutta) ve tipik olarak 32 bit (STM32, RP2, nRF, Unix x86) veya 64 bit (Unix x64) olabilir. Belirli nesne gösterimleri için sözcük boyutundan daha büyük de olabilir; örneğin OBJ_REPR_D, 32 bitlik bir mimaride 64 bit boyutunda bir mp_obj_t değerine sahiptir.
Bir mp_obj_t, bir MicroPython nesnesini temsil eder; örneğin bir tamsayı, kayan noktalı sayı, tip, dict veya sınıf örneği. Boole değerleri ve küçük tamsayılar gibi bazı nesneler değerlerini doğrudan mp_obj_t değerinde saklar ve ek bellek gerektirmez. Diğer nesneler değerlerini bellekte başka bir yerde (örneğin çöp toplanan yığında) saklar ve bunların mp_obj_t değeri o belleğe işaret eden bir işaretçi içerir. mp_obj_t değerinin bir kısmı, nesnenin hangi tipte olduğunu söyleyen etikettir (tag).
Kullanılabilir gösterimlerin ayrıntıları için py/mpconfig.h dosyasına bakın.
İşaretçi etiketleme (pointer tagging)
İşaretçiler sözcük hizalı olduğundan, bir mp_obj_t içinde saklandıklarında bu nesne tutamacının alt bitleri sıfır olacaktır. Örneğin 32 bitlik bir mimaride alt 2 bit sıfır olacaktır:
********|********|********|******00
Bu bitler bir etiket saklamak amacıyla ayrılmıştır. Etiket, bu bilgiyi saklamak için nesneye yeni bir alan eklemek yerine ek bilgi saklar; çünkü yeni alan eklemek verimsiz olabilir. MicroPython’da etiket, bir küçük tamsayı, içselleştirilmiş (interned) (küçük) bir karakter dizisi mi yoksa somut bir nesne mi ile uğraştığımızı söyler ve bunların her birine farklı anlamlar uygulanır.
Küçük tamsayılar için eşleme şöyledir:
********|********|********|*******1
Burada yıldız işaretleri gerçek tamsayı değerini tutar. İçselleştirilmiş bir karakter dizisi veya doğrudan bir nesne (örneğin True) için mp_obj_t değerinin düzeni sırasıyla şöyledir:
********|********|********|*****010
********|********|********|*****110
Yukarıdakilerin hiçbiri olmayan somut bir nesne ise şu biçimi alır:
********|********|********|******00
Buradaki yıldızlar somut nesnenin bellekteki adresine karşılık gelir.
Nesnelerin ayrılması¶
Bir küçük tamsayının değeri doğrudan mp_obj_t içinde saklanır ve yığında veya başka bir yerde değil, yerinde ayrılır. Bu nedenle, küçük tamsayıların oluşturulması yığını etkilemez. Metin verileri zaten başka bir yerde saklanan içselleştirilmiş karakter dizileri ve None, False ve True gibi doğrudan değerler için de durum benzerdir.
Somut bir nesne olan diğer her şey yığında ayrılır ve nesne yapısı, nesnenin tipini saklamak için nesne başlığında bir alan ayrılacak şekildedir.
+++++++++++
+ +
+ type + object header
+ +
+++++++++++
+ + object items
+ +
+ +
+++++++++++
Yığının en küçük ayırma birimi bir bloktur ve bu, dört makine sözcüğü boyutundadır (32 bitlik bir makinede 16 bayt, 64 bitlik bir makinede 32 bayt). Yığında ayrılan başka bir yapı da her bloktaki nesnelerin ayrılmasını izler. Bu yapıya bit eşlemi (bitmap) denir.
Bit eşlemi, bir bloğun “boş” mu yoksa “kullanımda” mı olduğunu izler ve her blok için bu durumu izlemek üzere iki bit kullanır.
İşaretle-süpür çöp toplayıcısı, yığında ayrılan nesneleri yönetir ve hâlâ kullanımda olan nesneleri işaretlemek için bit eşlemini de kullanır. Bu ayrıntıların tam uygulaması için py/gc.c dosyasına bakın.
Ayırma: yığın düzeni
Yığın, havuzlardaki bloklardan oluşacak şekilde düzenlenir. Bir bloğun farklı özellikleri olabilir:
ATB (ayırma tablosu baytı): Ayarlanmışsa, blok normal bir bloktur
FREE: Boş blok
HEAD: Bir blok zincirinin başı
TAIL: Bir blok zincirinin kuyruğunda
MARK : İşaretlenmiş baş blok
FTB (sonlandırıcı tablosu baytı): Ayarlanmışsa, bloğun bir sonlandırıcısı vardır