Skip to content

Python Bellek Yönetimi (Memory Management)

Python Bellek Yönetimi (Memory Management)

Bu yazımızda Python Nasıl Çalışır ve Python'da Bellek Yönetimi(Memory Management) konusunu anlamaya çalışacağız.

Python'da bellek yönetimi nedir?

Bellek yönetimi, Python'un programınız tarafından kullanılan belleği nasıl işlediğini ve düzenlediğini ifade eder. Değişkenler ve nesneler için bellek ayırmayı, kullanımlarını takip etmeyi ve artık gerekmediğinde belleği boşaltmayı içerir.

Bellek yönetimi neden önemlidir?

Uygun bellek yönetimi, Python programlarınızın verimli ve optimum performansı için çok önemlidir. Bellek etkin bir şekilde yönetilmezse, programınız gereğinden fazla bellek tüketerek yavaşlamalara, çökmelere ve hatta belleğin tükenmesine neden olabilir.

Python'da bellek yönetimi nasıl çalışır?

Python, belleği işlemek için "otomatik bellek yönetimi" veya garbage collection ("çöp toplama") adı verilen bir teknik kullanır. Çöp toplayıcı(Garbage collector), programınızda oluşturulan tüm nesneleri takip eder ve artık kullanılmayanları tanımlar. Daha sonra, kullanılmayan nesnelerin kapladığı belleği boşaltarak ileride kullanılmak üzere hazır hale getirir.

Bellek yönetimi ne zaman gerçekleşir?

Bellek yönetimi Python'da otomatik olarak gerçekleşir. Bir nesneye artık programınızdaki herhangi bir değişken tarafından başvurulmadığı zaman, çöp toplayıcı nesneyi temizleme için uygun olarak tanımlar ve belleği geri alır.

Bellek yönetimi sorunları nerede ortaya çıkabilir?

Python programınızda nesneler oluşturup kullandığınızda bellek yönetimi sorunları oluşabilir. Örneğin, bir e-ticaret projesinde, bellekte depolanan çok sayıda ürün nesneniz varsa, ancak artık ihtiyaç duyulmayan ürünler için belleği uygun şekilde serbest bırakmıyorsanız, bu, bellek şişmesine ve performans sorunlarına yol açabilir.

Şimdi biraz daha detaylı inceleme yapalım ve bellek yönetiminin temellerini anlamaya çalışalım.

Python Nedir? sorusu ile başlayalım. Çok mu geriden başladık :-) ?

Python aslında Türkçe gibi, İngilizce gibi kuralları, söz dizimi(sytax) olan bir dildir. İnsanların iletişim kurmak için kullandığı dillerden farklı olarak Python, bilgisayarın anlayacağı bir dildir. Bir programlama dilidir. CPython Nedir?

Python programlama dili ile yazılan kodları derleyen(compile) ve yorumlayan(interpreting) bir python implementation(icra edici, yürütme aracıdır.) C programalama dili ile yazılmıştır ve en yaygın kullanılan python implementasyonudur. CPython, python kodunu derleyerek baytkodu(bytecode) üretir. Daha sonra bu baytkodu Python Sanal Makinesi(Python Virtual Machine) tarafından yorumlanır ve çalıştırılır. (Üstteki resimden görebilirsiniz) Bayt kodu, Python programının sanal makine tarafından yürütülmek üzere optimize edilmiş düşük düzeyli(low level) bir temsilidir. CPython gibi başka python implementasyonları da mevcuttur. IronPython, Microsoft'un Ortak Dil Çalışma Zamanı üzerinde çalışmak üzere derlenir. Jython, Java Sanal Makinesinde çalıştırmak için Java bayt kodunu derler. CPython'da PyObject Nedir ?

Tüm Python nesnelerinin(objects) dedesi olan PyObject bir yapı(struct)dır.

#ifndef PyObject_HEAD
#define PyObject_HEAD                   \
    _PyObject_HEAD_EXTRA                \
    Py_ssize_t ob_refcnt;               \
    struct _typeobject *ob_type;
#endif

typedef struct _object {
    PyObject_HEAD
} PyObject;
  • Bu kod parçasını anlamak için kendimizi zorlamamız gerekmiyor. Sadece ob_refcnt(object reference counter) ve ob_type(object type) alanlarına odaklanalım.
  • ob_refcnt nesnenin referans sayısıdır. Bu, nesneye kaç referans olduğunu takip eden bir sayıdır. Referans sayısı 0'a ulaştığında, nesne silinir.
  • ob_type nesnenin tip nesnesine bir pointer'dır. Bu, nesnenin tipi hakkında bilgi içeren bir yapıdır, örneğin adı, boyutu ve methodları.

Şimdi basit bir örnek üzerinden yeni oluşturulan bir nesnenin referans sayısını görelim

import sys


def mydemo():
    #  create simple object
    ob1 = object()
    # sys.getrefcount çağırıldığında nesneye geçici bir referans oluşturur bu yüzden 1 ile azalttık.
    ref_count = sys.getrefcount(ob1) - 1
    print(f"The reference count of the object is {ref_count}")

    ob2 = ob1
    ob3 = ob1
    ref_count = sys.getrefcount(ob1) - 1
    print(f"The reference count of the object is {ref_count}")
    print("Deleting object 3 ...")
    del ob3
    ref_count = sys.getrefcount(ob1) - 1
    print(f"The reference count of the object is {ref_count}")


if __name__ == "__main__":
    mydemo()
- sys modülüne ait getrefcount fonksiyonu ile bir objenin referans adedini alabiliyoruz. sys.getrefcount çağırıldığında nesneye geçici bir referans oluşturur bu yüzden 1 ile azalttık. object() diyerek yeni bir obje oluşturduk ve referansını ob1'e atama yaptık. ob2 ve ob3 değişkenlerine de aynı nesnenin referansını(ob1) atama yaptık sırası ile referans adedini (ref_count) gösterdik obj3 referansını silince ob_refcnt 1 adet azalmış oldu. Yukarıdaki kodun çıktısı aşağıdaki gibidir
The reference count of the object is 1
The reference count of the object is 3
Deleting object 3 ...
The reference count of the object is 2

Python'da bellek yönetimini nasıl optimize edebiliriz?

Bellek dostu olmayan pratikler

  1. Gereksiz yere büyük listeler oluşturma: Yalnızca birkaç öğeye ihtiyacınız varken çok sayıda öğe içeren bir liste oluşturursanız, gereksiz yere çok fazla bellek tüketebilir. Örneğin bir e-ticaret sitesinde sadece birkaç tanesini göstermeniz gerekse bile tüm ürün isimlerinden oluşan bir liste oluşturmak.

  2. Verimsiz özyineleme(recursion): Uygun bir sonlandırma koşuluna sahip olmayan özyinelemeli işlevler, aşırı bellek kullanımına yol açabilir. Örneğin, kendini bir bitiş koşulu olmadan tekrar tekrar çağıran özyinelemeli bir işlev.

  3. Nesnelerin gereksiz kopyaları: Nesnelerin, özellikle de büyük olanların gereksiz kopyalarının oluşturulması aşırı bellek tüketebilir. Örneğin, referanslarla çalışmak yerine tüm ürün veritabanını birden çok kez belleğe kopyalamak.

  4. Yayınlanmamış kaynaklar: Dosya tanıtıcıları veya ağ bağlantıları gibi kaynakların serbest bırakılmaması, bellek sızıntılarına(memory leak) neden olabilir. Örneğin, dosyaları bir döngüde açıp kullandıktan sonra kapatmamak.

  5. Global değişkenleri yoğun bir şekilde kullanma: Global değişkenler, artık gerekmeseler bile programın yürütülmesi boyunca bellekte kalır. Bu, gereksiz bellek kullanımına yol açabilir.

  6. Büyük miktarda veriyi bellekte depolama: Tüm verileri bir kerede yükleyip belleğe kaydetmeniz, belleğin aşırı yüklenmesine neden olabilir. Örneğin, tüm ürün görsellerini talep üzerine yüklemek yerine aynı anda belleğe yüklemek.

  7. Döngüler içinde gereksiz nesneler oluşturma: Döngüler içinde dışarıya taşınabilen nesneler oluşturmak, gereksiz bellek tahsisine yol açabilir. Örneğin, var olan bir döngüyü yeniden kullanmak yerine bir döngünün her yinelemesinde yeni bir nesne örneği oluşturmak.

  8. Kullanılmayan nesnelere yapılan başvuruları tutma: Artık ihtiyaç duyulmayan nesnelere yapılan referansları silmezseniz, bunlar çöp olarak toplanmaz ve bellek israfına neden olur.

  9. Büyük veri yapılarının gereksiz kullanımı: Daha basit olanların yeterli olacağı durumlarda sözlükler veya kümeler gibi karmaşık veri yapılarının kullanılması gereksiz bellek kullanımına neden olabilir.

  10. Dairesel referanslar: Dairesel referanslar, nesneler bir döngüde birbirlerine başvurduğunda otomatik olarak serbest bırakılmalarını önlediğinde ortaya çıkar. Bu, bellek sızıntılarına neden olabilir.

Bellek dostu pratikler

  1. Listeler yerine generator veya iterator kullanma: Generator ve iterator, verileri her seferinde bir öğeyle işlemenize olanak tanıyarak bellek tüketimini azaltır. Örneğin, büyük bir liste oluşturmak yerine ürün adlarını yinelemek için bir generator kullanmak.

  2. Yeni nesneler oluşturmak yerine nesneleri yeniden kullanma: Yeni nesneler oluşturmak yerine, bellek tahsisini en aza indirmek için mevcut nesneleri değiştirebilirsiniz. Örneğin, her ürün için yeni bir nesne oluşturmak yerine bir ürün nesnesini yeniden kullanmak ve özelliklerini güncellemek.

  3. Verimli dosya işleme: Kaynakları serbest bırakmak ve bellek sızıntılarını önlemek için kullanımdan sonra dosyaların kapatılması.

  4. Context manager kullanma: Context manager (with ile birlikte), kullanımdan sonra kaynakları otomatik olarak serbest bırakarak etkin bellek yönetimi sağlar. Örneğin, veritabanı bağlantılarını yönetmek için bir context manager kullanmak.

  5. Değişkenlerin kapsamını sınırlama: Değişkenlerin kapsamını yalnızca ihtiyaç duyuldukları yere kadar küçültmek, artık kullanılmadıklarında belleği boşaltmaya yardımcı olur.

  6. Lazy loading kullanma: Her şeyi önceden yüklemek yerine verileri yalnızca gerektiğinde belleğe yüklemek bellek tüketimini azaltır.

  7. Kullanılmayan nesneleri silme: Artık gerekmeyen nesneleri açık bir şekilde silmek, bellek kaynaklarını boşaltabilir. Örneğin, değişkenleri silmek için del ifadesini kullanmak.

  8. Büyük veri kümelerini parçalar halinde yönetme: Büyük veri kümelerini daha küçük parçalar halinde işlemek, belleğin aşırı yüklenmesini önlemeye yardımcı olabilir.

  9. Uygun veri yapılarını kullanma: Eldeki görev için doğru veri yapısını seçmek, bellek kullanımını optimize edebilir. Örneğin sıralama önemli değilken sözlük(dict) yerine liste kullanmak.

  10. Döngüsel referanslardan kaçınma: Nesne referanslarına dikkat etmek ve nesneler arasında döngüsel bağımlılıklardan kaçınmak, bellek sızıntılarını önleyebilir.