Bellek Sızıntısı Tespiti ve Profilleme: Uygulamanız Neden Şişiyor?

bellek-sizintisi-tespiti-35

Uzun süre çalışan bir servis ilk gün kusursuz, üçüncü gün ise ağır davranıyorsa şüpheli genellikle CPU değil bellektir. Bellek sızıntısı, artık ihtiyaç duyulmayan nesnelerin hâlâ erişilebilir kalması veya işletim sistemi kaynaklarının serbest bırakılmaması durumudur. Bu sorun, yalnızca uygulamayı yavaşlatmaz; konteynerin OOM Killer tarafından sonlandırılmasına, gecikmelerin artmasına ve maliyetlerin yükselmesine de yol açabilir. `` Bellek tüketimini anlamak için önce iki kavramı ayırmak gerekir: anlık kullanım ve büyüme eğilimi. Bir uygulamanın 800 MB RAM kullanması tek başına hata değildir; trafik yükü, önbellek ve çalışma veri seti bunu gerektirebilir. Asıl kritik soru şudur: İş yükü sabitken bellek taban çizgisi zamanla yükseliyor mu?

Kabaca, süreç belleğini şu şekilde modelleyebiliriz:

\[M(t) = M_0 + C(t) + L(t)\]

Burada $M_0$ başlangıç maliyeti, $C(t)$ meşru önbellek veya geçici veri kullanımı, $L(t)$ ise sızıntı bileşenidir. Sağlıklı bir sistemde geçici işlemler bittiğinde $C(t)$ azalır. Sızıntı varsa $L(t)$ sürekli büyür ve uzun vadeli eğim $\frac{dM}{dt} > 0$ kalır.

Durum Gözlenen davranış Olası yorum
Sağlıklı dalgalanma Yük altında yükselir, GC sonrası düşer Geçici nesneler normal temizleniyor
Önbellek ısınması Başta artar, sonra plato yapar Sınırlandırılmış cache davranışı
Gerçek sızıntı Trafik sabitken düzenli yükseliş Referans veya kaynak tutuluyor
Parçalanma Nesneler temizlense de RSS düşmez Ayırıcı/işletim sistemi davranışı olabilir

Sızıntının Kaynağını Sınıflandırın

Yönetilen dillerde çöp toplayıcı, erişilebilen nesneleri silmez. Dolayısıyla sızıntı; global koleksiyonlar, büyüyen event listener listeleri, kapanmayan coroutine görevleri ya da sınırsız cache’ler yüzünden oluşabilir. Buna karşılık dosya tanıtıcıları, socket’ler ve native buffer’lar çoğu zaman GC’nin doğrudan çözemediği kaynaklardır.

Kaynak türü Tipik belirti İnceleme yaklaşımı
Heap nesneleri Heap snapshot boyutu büyür Retaining path ve nesne sayısı
Native bellek RSS artar, heap sabit kalır Allocator ve sistem metrikleri
Dosya tanıtıcısı too many open files hatası FD sayısı, kapatma akışı
Cache Anahtar sayısı büyür TTL, LRU ve üst sınır kontrolü

Ölç, Karşılaştır, Doğrula

En güvenilir yöntem, aynı senaryonun farklı anlarında alınmış iki veya daha fazla profili karşılaştırmaktır. Önce uygulamayı kontrollü bir yük altında çalıştırın. Ardından bir “başlangıç” snapshot’ı, birkaç istek döngüsünden sonra ikinci snapshot’ı ve mümkünse zorlanmış bir GC sonrası üçüncü snapshot’ı alın. GC sonrasında yaşayan nesneler özellikle değerlidir; bunlar hâlâ bir referans zinciri tarafından tutuluyordur.

Python’da tracemalloc, tahsisatların hangi satırlardan geldiğini izlemek için pratik bir başlangıç aracıdır:

import tracemalloc

tracemalloc.start(25)  # Çağrı zincirinin 25 karesini saklar.
baslangic = tracemalloc.take_snapshot()

for _ in range(10_000):
    istek_isle()  # Şüpheli iş yükü burada tekrar edilir.

son = tracemalloc.take_snapshot()
for fark in son.compare_to(baslangic, "lineno")[:5]:
    print(fark)

Bu kod, en çok bellek artışı üreten satırları listeler. Ancak bir satırın tahsisat yapması onu otomatik olarak suçlu yapmaz. Asıl soru, o nesnelerin neden yaşamaya devam ettiğidir. Heap snapshot araçlarındaki retainers, dominators veya retaining path görünümleri bu nedenle önemlidir: Nesneyi hayatta tutan global değişkeni, closure’ı ya da cache girdisini gösterir.

Üretimde yalnızca profil almak yerine gözlemlenebilirlik kurun. RSS, heap used, GC pause süresi, açık dosya tanıtıcısı sayısı ve cache boyutunu zaman serisi olarak izleyin. Alarm koşulu mutlak bir sayıdan çok eğim olabilir: örneğin sabit trafikte bir saat boyunca sürekli pozitif bellek artışı inceleme gerektirir.

Son olarak, her artışı sızıntı diye etiketlemeyin. JIT derleyici, bellek havuzları ve allocator parçalanması RSS’i yüksek tutabilir. Teşhis tamamlandığında çözüm; referansı kaldırmak, listener’ı unsubscribe etmek, close çağrısını garantiye almak, cache’e TTL/kapasite koymak ve aynı yük testiyle eğrinin yeniden plato yaptığını doğrulamak olmalıdır. Bellek avcılığında kanıt, tek bir grafik değil; tekrar edilebilir deneydir.

Yorumlar