Python'da bellek sızıntısı, çöp toplayıcı (garbage collector) çalışmasına rağmen bir programın kullandığı bellek miktarının zamanla sürekli artması ve bir türlü düşmemesidir. Bunun temel nedeni, artık ihtiyaç duyulmayan nesnelere hâlâ bir yerden referans tutulması; yani Python'ın "bu nesneye artık kimse bakmıyor" diyemediği durumlardır. Özellikle uzun süre çalışan servislerde (API sunucuları, arka plan işçileri, sürekli açık kalan betikler) bu birikim saatler içinde fark edilmeyip günler sonra bellek yetersizliği hatasına dönüşebilir. Bu yazıda sızıntının neden oluştuğunu, en sık görülen senaryoları ve tracemalloc ile nasıl tespit edileceğini adım adım ele alıyoruz.
Python'da Çöp Toplayıcı Var Ama Bellek Neden Artar?
Python'ın bellek yönetimi iki mekanizmaya dayanır: referans sayımı ve döngüsel çöp toplayıcı. Bir nesneye tutulan referans sayısı sıfıra düştüğü anda o nesnenin kapladığı bellek anında serbest bırakılır. Referans sayımının tek başına çözemediği durumlar için ise arka planda periyodik olarak çalışan döngüsel çöp toplayıcı devreye girer ve birbirine referans veren ama dışarıdan artık erişilemeyen nesne gruplarını tespit edip temizler.
Burada sık düşülen yanılgı şudur: "Python otomatik çöp topluyorsa bellek sızıntısı zaten yaşanmaz." Bu doğru değildir. Çöp toplayıcı yalnızca erişilemeyen nesneleri temizler; bir nesneye hâlâ canlı bir referans varsa — bu referans bilinçli bir tasarım kararı olsa da, unutulmuş bir liste elemanı olsa da — o nesne bellekte kalmaya devam eder. Yani sızıntı, Python'ın bir hatası değil, programın nesneleri gereğinden uzun süre "canlı" tutmasıdır.
Pratikte bu, genellikle şu şekillerde ortaya çıkar: bir fonksiyon her çağrıldığında global bir yapıya veri ekler ama hiç silmez; bir önbellek büyümeye devam eder ama sınırı yoktur; ya da bir callback, kapsayan nesneye referans tutar ve o nesnenin serbest kalmasını engeller. Her durumda ortak nokta aynıdır: referans zinciri kopmadığı için bellek geri dönmez.
Bunu anlamanın en pratik yolu, bellek yönetimini "kim bu nesneye hâlâ bakıyor?" sorusuyla düşünmektir. Sızıntı arayan bir geliştirici aslında bellek arıyor değil, unutulmuş referans arıyordur. Bu bakış açısı, ilerleyen bölümlerdeki tespit tekniklerinin de temelini oluşturur; çünkü araçların çoğu doğrudan "bellek kullanımı"nı değil, "hangi nesne kaç kez ve nereden referanslanıyor" bilgisini gösterir. Konunun kavramsal çerçevesi net olduğunda, sızıntı senaryolarını tanımak ve kod içinde ayıklamak çok daha kolay hale gelir.
Yaygın 3 Bellek Sızıntısı Senaryosu

Sahada karşılaşılan bellek sızıntılarının büyük çoğunluğu üç kalıptan birine girer. Bu kalıpları tanımak, hatayı ararken zaman kaybetmemek için önemlidir; çünkü her biri farklı bir belirti ve farklı bir tespit yöntemi gerektirir.
İlk ve en sık görülen senaryo, modül seviyesinde tanımlanmış bir global liste veya sözlüğün sürekli büyümesidir. Örneğin bir istek geldikçe log kaydının veya işlenmiş verinin bir listeye eklenip hiç temizlenmemesi, süreç kapanana kadar bellekte kalıcı bir birikime yol açar. İkinci kalıp, döngüsel referanslarla birlikte __del__ metodunun kullanılmasıdır; nesneler birbirine referans verdiğinde döngüsel çöp toplayıcı bunları normalde temizleyebilir, ancak __del__ tanımlı nesnelerde temizleme sırası belirsizleşebilir ve nesneler beklenenden uzun süre bellekte asılı kalabilir. Üçüncü kalıp ise kapatılmayan kaynaklardır: açılan dosya tanıtıcıları, veritabanı bağlantıları veya ağ soketleri close() çağrılmadan bırakıldığında, hem bellek hem işletim sistemi kaynağı sızdırılır.
Bu üç senaryoyu ayırt etmek için aşağıdaki tablo pratik bir referans sunar:
| Senaryo | Belirti | Tespit Yöntemi |
|---|---|---|
| Global liste/sözlük büyümesi | Bellek zamanla doğrusal artar, düşmez | tracemalloc ile snapshot karşılaştırma |
| Döngüsel referans + __del__ | gc.collect() sonrası bile nesne sayısı azalmaz | gc.get_objects() ile nesne sayımı |
| Kapatılmayan kaynaklar | Dosya/soket sayısı sürekli artar, "too many open files" hatası | İşletim sistemi seviyesinde açık tanıtıcı sayımı |
Bu kalıpların ortak paydası, kodun "geçici" sandığı bir yapının aslında kalıcı hale gelmesidir. Bu tür hataları erken fark etmek, kodu yazarken referans yaşam döngüsünü bilinçli tasarlamayı gerektirir; birebir Python dersleri kapsamında bu tür bellek yönetimi alışkanlıkları, öğrencilere gerçek kod örnekleri üzerinden adım adım gösterilir. Bir sonraki bölümde, bu senaryoların en yaygını olan global liste birikimini tracemalloc ile nasıl somut biçimde gözlemleyebileceğinizi görebilirsiniz.
tracemalloc ile Bellek Kullanımını İzleme
Python'ın standart kütüphanesinde yer alan tracemalloc modülü, hangi kod satırının ne kadar bellek ayırdığını satır satır izlemenizi sağlar. Sızıntı ararken en değerli özelliği, iki farklı zaman noktasında alınan "anlık görüntüleri" (snapshot) karşılaştırarak, aradaki farkta bellek artışına en çok katkı yapan satırları doğrudan listeleyebilmesidir.
Kullanım akışı üç adımdan oluşur:
- İzlemeyi
tracemalloc.start()ile başlatmak. - Şüpheli işlem öncesi ve sonrası birer
take_snapshot()almak. - İki snapshot'ı
compare_to()ile kıyaslayıp en büyük farkı gösteren satırları incelemek.
Aşağıdaki örnek, global bir listeye sürekli veri eklenen bir fonksiyonun bellek etkisini nasıl gözlemleyebileceğinizi gösterir:
import tracemalloc
tracemalloc.start()
_cache = []
def islem_yap(veri):
_cache.append(veri * 1000)
snapshot1 = tracemalloc.take_snapshot()
for i in range(10000):
islem_yap(str(i))
snapshot2 = tracemalloc.take_snapshot()
top_stats = snapshot2.compare_to(snapshot1, "lineno")
for stat in top_stats[:3]:
print(stat)
Bu kodu çalıştırdığınızda çıktı, _cache.append satırının bellek artışının büyük kısmından sorumlu olduğunu açıkça gösterecektir. Gerçek bir uygulamada bu karşılaştırmayı, şüphelendiğiniz bir isteğin öncesinde ve sonrasında yaparsanız, hangi fonksiyonun beklenmedik şekilde bellek biriktirdiğini dakikalar içinde ayırt edebilirsiniz. tracemalloc'ın satır bazlı görünürlüğü, tahmin yürütmek yerine ölçülebilir veriyle ilerlemenizi sağlar; bu da özellikle uzun süre çalışan servislerde saatler süren "acaba nerede" arayışını kısa bir kıyaslama işlemine indirger.
Hatalı ve Düzeltilmiş Kod: Global Listeye Sürekli Ekleme
Bellek sızıntılarının en sık rastlanan biçimlerinden biri, oldukça sıradan görünen bir alışkanlıktan doğar: bir modülün en üstünde tanımlanmış global bir listeye, uygulama çalıştıkça sürekli eleman eklemek. Özellikle istek işleyen servislerde, her gelen isteğin sonucunu "sonradan lazım olur" diye bir listeye atmak, kısa vadede hiçbir sorun çıkarmaz. Uygulama saatlerce, günlerce ayakta kaldığında ise bu liste durmadan büyür ve process'in bellek kullanımı grafikte düz bir çizgi yerine sürekli yükselen bir eğri hâline gelir.
Aşağıdaki örnek, bu hatanın tipik görünümünü gösteriyor:
sonuc_kayitlari = []
def istek_isle(istek):
sonuc = agir_hesaplama(istek)
sonuc_kayitlari.append(sonuc)
return sonuc
Burada sonuc_kayitlari listesi hiçbir zaman temizlenmiyor. Fonksiyon her çağrıldığında liste bir eleman daha büyüyor ve çöp toplayıcının bu listeye erişimi olduğu sürece içindeki hiçbir nesne serbest bırakılmıyor. Sorun çöp toplayıcının çalışmamasında değil; hâlâ referans edilen veriyi tutmaya devam etmesinde.
Düzeltilmiş hâlde, listenin sınırsız büyümesine izin vermek yerine belirli bir kapasite tanımlanır. Python'ın standart kütüphanesindeki deque yapısı, maxlen parametresiyle tam bu ihtiyaca cevap verir:
from collections import deque
SON_ISTEK_SAYISI = 500
sonuc_kayitlari = deque(maxlen=SON_ISTEK_SAYISI)
def istek_isle(istek):
sonuc = agir_hesaplama(istek)
sonuc_kayitlari.append(sonuc)
return sonuc
Bu versiyonda yeni bir eleman eklendiğinde, liste zaten SON_ISTEK_SAYISI kadar eleman taşıyorsa en eski kayıt otomatik olarak atılır. Bellek kullanımı belirli bir seviyede sabitlenir ve uygulama ne kadar uzun süre çalışırsa çalışsın bu yapı büyümeye devam etmez. Aynı mantık, dosyaya periyodik yazıp listeyi boşaltmak veya belirli bir zaman aralığından eski kayıtları silmek gibi farklı yöntemlerle de uygulanabilir; önemli olan, "sonsuza kadar büyüyebilen" bir yapı yerine sınırları baştan tanımlanmış bir yapı kurmaktır. Bu tür kalıcı veri yapılarının nerede ve nasıl kurulacağına dair karar verme pratiği, tek başına okuyarak değil örnekler üzerinde çalışarak oturur; kayıttan izlenebilen Python dersleri bu tarz gerçek senaryoları adım adım işleyerek bu alışkanlığı erken kazandırmayı hedefler.
Basit İzleme Alışkanlıkları

Bellek sızıntılarının çoğu, karmaşık algoritmalardan değil küçük alışkanlık eksikliklerinden doğar. Bu yüzden ileri düzey araçlara geçmeden önce, günlük yazım pratiğine yerleştirilebilecek birkaç basit davranış büyük fark yaratır.
İlk ve en temel alışkanlık, dosya, ağ bağlantısı veya veritabanı oturumu gibi kaynaklarla çalışırken with bloklarını kullanmaktır. Bu yapı, blok içindeki işlem bitince veya bir hata oluşsa bile ilgili kaynağın kapatılmasını garanti eder; kaynağı elle kapatmayı unutmak, zamanla sistem kaynaklarının tükenmesine yol açan yaygın bir nedendir.
with open("kayit.log", "a") as dosya:
dosya.write("islem tamamlandi ")
İkinci alışkanlık, birbirine karşılıklı referans veren nesneler kurarken weakref modülünü değerlendirmektir. Örneğin bir ebeveyn nesne çocuklarını bir listede tutuyor ve her çocuk da ebeveynine geri referans veriyorsa, bu döngüsel referans zincirlerinden birini zayıf referansa çevirmek, çöp toplayıcının nesneleri döngü tespit turuna kadar beklemeden serbest bırakmasına yardımcı olur. Özellikle önbellek veya gözlemci (observer) desenleri kuran kodlarda bu tercih doğrudan bellek davranışını etkiler.
Üçüncü alışkanlık, geliştirme ve test aşamasında nesne sayısını izlemektir. gc.get_objects() fonksiyonunun döndürdüğü listenin uzunluğunu belirli aralıklarla loglamak, hangi noktadan sonra nesne sayısının anormal şekilde arttığını erken fark etmeyi sağlar:
import gc
print("Toplam izlenen nesne sayisi:", len(gc.get_objects()))
Son olarak, bu kontrolleri tek seferlik değil periyodik bir rutine dönüştürmek gerekir. Uzun süre çalışan bir servis için haftalık ya da yük testi sonrası düzenli bellek kontrolü, sızıntıyı üretim ortamında değil geliştirme aşamasında yakalamanın en pratik yoludur. Bu tür kod okuma ve hata ayıklama refleksinin ne kadar güçlü olduğunu görmek isteyenler, ücretsiz kodlama bilgisi testi ile mevcut seviyelerini hızlıca ölçebilir.
Bellek Sızıntısını Önleme Adımları
Bellek sızıntısını tamamen ortadan kaldırmak, tek bir araç ya da tek bir kural meselesi değildir; kodun yazılış biçiminden izleme alışkanlığına kadar birkaç adımın bir arada uygulanmasıyla mümkün olur. Aşağıdaki sıralama, uzun süre çalışan Python servislerinde takip edilebilecek pratik bir kontrol listesidir.
- Dosya, soket ve veritabanı bağlantısı gibi tüm kaynakları
withbloklarıyla açıp kapatın; kaynağı elle kapatmayı unutma riskini koddan tamamen çıkarın. - Sürekli veri biriktiren yapıları (liste, sözlük, önbellek) baştan sınırlı boyutlu kurun;
deque(maxlen=...)veya süreye/kapasiteye dayalı tahliye mantığı olan bir önbellek yapısı tercih edin. - Birbirine karşılıklı referans veren nesnelerde en az bir yönü
weakrefile tanımlayın, böylece döngüsel referans çöp toplayıcının ek yüküne bağımlı kalmaz. - Geliştirme ortamında
tracemallocvegc.get_objects()gibi araçlarla düzenli anlık görüntü alarak nesne sayısındaki artış eğilimini erken fark edin. - Üretime çıkmadan önce yük testi sırasında bellek kullanımını zaman içinde grafikleyin; düz bir çizgi yerine sürekli yükselen bir eğri görürseniz kaynağı bulana kadar dağıtımı ertelemeyi düşünün.
- Bu kontrolleri tek seferlik bir denetim değil, kod incelemesinin (code review) rutin bir parçası hâline getirin.
Bu adımların her biri tek başına küçük bir disiplin gibi görünse de bir araya geldiğinde uzun vadeli kararlılık sağlar. Bellek yönetimini bu seviyede kavramak, aslında daha genel bir yazılım okuryazarlığının parçasıdır; kaynak yönetimi, veri yapısı seçimi ve performans analizi gibi konularda kendinizi nerede gördüğünüzü netleştirmek isterseniz ücretsiz kariyer testi bu yönde bir başlangıç noktası olabilir.
Sızıntı mı, Normal Bellek Artışı mı? Önbellekleme Ayrımı
Bir uygulamanın bellek kullanımının artması her zaman bir soruna işaret etmez. Özellikle önbellekleme (caching) amacıyla bilinçli olarak veri tutan sistemlerde bellek artışı, tasarımın bir parçasıdır ve bu artışı sızıntıyla karıştırmak gereksiz yere kod değiştirmeye, hatta işlevsel bir optimizasyonu bozmaya yol açabilir. Ayrımı doğru yapmak, hem zaman kaybını önler hem de gerçek sorunların gözden kaçmasını engeller.
Önbellekleme ile sızıntı arasındaki temel fark, bellek artışının bir sınırı olup olmadığıdır. İyi tasarlanmış bir önbellek belirli bir boyuta ulaştığında ya eski verileri siler ya da yeni veri eklemeyi durdurur. Bellek kullanımı bir noktadan sonra yatay bir çizgi gibi sabitlenir. Sızıntıda ise böyle bir sınır yoktur; uygulama ne kadar uzun çalışırsa bellek o kadar artar ve grafikte sürekli yükselen bir çizgi görülür.
Bu ayrımı pratikte yapmanın birkaç yolu vardır:
- Uygulamayı sabit ve tekrar eden bir yükle (örneğin aynı isteği defalarca göndererek) belirli bir süre çalıştırın ve bellek kullanımını zaman içinde gözlemleyin.
- Önbellek kullanılıyorsa, önbelleğin boyut sınırı (maksimum eleman sayısı, TTL gibi bir süre sınırı) olup olmadığını koddan kontrol edin.
- Bellekte tutulan nesnelerin sayısını periyodik olarak loglayın; sayı sabitleniyorsa önbellekleme, sürekli artıyorsa sızıntı olasılığı yüksektir.
tracemallocile alınan iki anlık görüntüyü (snapshot) karşılaştırarak hangi satırların bellek tutmaya devam ettiğini görün; bu satırlar bilinçli bir önbellek yapısına mı yoksa unutulmuş bir referansa mı ait, kaynak koddan anlaşılır.
Kısacası, bellek artışının kendisi değil, artışın sınırsız ve amaçsız olup olmadığı asıl belirleyicidir. Sınırlı, kontrollü ve iş mantığına hizmet eden bir bellek kullanımı sorun değildir; sınırsız ve büyümeye devam eden bir kullanım ise er ya da geç uygulamanın çökmesine neden olur.
Sık Sorulan Sorular
Python'da garbage collector varken bellek sızıntısı gerçekten olur mu?
Evet. Çöp toplayıcı yalnızca artık erişilemeyen nesneleri temizler; bir nesneye hâlâ bir yerden (global bir liste, önbellek sözlüğü, kapatılmamış bir referans) erişim varsa, o nesne kullanılmıyor olsa bile bellekte kalmaya devam eder. Bu nedenle garbage collector'ın varlığı bellek sızıntısını imkânsız kılmaz, sadece belirli bir sınıf sorunu (tamamen erişilemez hale gelmiş nesneler) otomatik olarak çözer.
tracemalloc kullanmak uygulamayı yavaşlatır mı?
tracemalloc çalışırken bir miktar ek yük getirir çünkü bellek ayırma işlemlerini izler. Bu yüzden genellikle geliştirme ve hata ayıklama aşamalarında, gerektiğinde açılıp kapatılacak şekilde kullanılması, sürekli üretim ortamında açık bırakılmasından daha sağlıklı bir yaklaşımdır.
Döngüsel referanslar neden bazen sızıntıya yol açar?
Döngüsel referanslar (iki nesnenin birbirini işaret etmesi) modern Python sürümlerinde döngüsel çöp toplayıcı tarafından tespit edilip temizlenebilir. Ancak nesnelerden biri özel bir temizleme davranışı tanımlıyorsa veya döngü çok karmaşık ve derinse, temizleme gecikebilir ya da beklenenden geç gerçekleşebilir. Bu gecikme, kısa ömürlü olması beklenen nesnelerin uzun süre bellekte kalmasına ve bunun sızıntı gibi görünmesine neden olabilir.
Uzun süre çalışan bir serviste belleği ne sıklıkla kontrol etmeliyim?
Kesin bir sıklık her uygulama için farklıdır, ancak genel bir yaklaşım olarak servisin normal yük altında belirli aralıklarla (örneğin belirli sayıda istek veya belirli bir zaman diliminde) bellek kullanımını loglaması ve bu logların düzenli olarak gözden geçirilmesi yeterlidir. Önemli olan, artışın bir örüntü oluşturup oluşturmadığını görebilecek kadar sık ölçüm yapmaktır.
Önbellekleme ile bellek sızıntısını nasıl ayırt ederim?
En net yöntem, bellek kullanımının bir sınıra ulaşıp ulaşmadığını gözlemlemektir. Önbellekleme belirli bir boyutta sabitlenir veya eski veriyi temizler; sızıntıda bellek kullanımı sürekli ve sınırsız şekilde artmaya devam eder. Bunu görmek için uygulamayı sabit bir yükle belirli bir süre çalıştırıp bellek grafiğinin yatay mı yoksa sürekli yükselen bir çizgi mi oluşturduğuna bakmak yeterlidir.
Bellek sızıntıları çoğu zaman gösterişli hatalar değil, sessizce biriken küçük ihmallerdir; global bir listeye unutulan bir ekleme, kapatılmayan bir bağlantı ya da sınırsız büyüyen bir önbellek. Bu tür sorunları erken yakalamanın yolu, kodu yazarken nesnelerin yaşam döngüsünü düşünme alışkanlığından geçer. Bu alışkanlığı ve tracemalloc gibi araçları pratik örneklerle adım adım pekiştirmek isteyenler için Python özel ders programımız, bellek yönetimi dahil gerçek dünya senaryolarını birebir çalışma imkânı sunar.