« Tüm yayınlar

Rust servislerinde bellek neden düşmüyor: glibc, jemalloc ve munmap

Bir Rust servisinde yük testi sonrası RSS düşmüyordu. Sorun bellek sızıntısı değil, glibc'nin arena tabanlı bellek yönetimiydi; jemalloc'a geçiş çözümü sağladı.

Bir ekip, olay tabanlı (Kafka/Redis Streams/NATS tüketen) bir Rust servisinde yük testi sırasında RSS'in yük bittikten sonra da yüksek kalıp inmediğini fark ediyor. dhat ile yapılan analiz, heap belleğin program içinde neredeyse tamamen serbest bırakıldığını, yani klasik anlamda bir bellek sızıntısı olmadığını gösteriyor. Sorun, glibc'nin ptmalloc ayırıcısının arena tabanlı çalışma biçiminden kaynaklanıyor: Tokio görevlerinin bellek blokları arenalar içinde iç içe geçmiş şekilde yerleşiyor ve heap yalnızca en üstten (top chunk) küçültülebiliyor. Kısa ömürlü görevler serbest kalsa bile, henüz konsolide edilmemiş serbest bloklar veya hâlâ canlı olan bir blok, altındaki tüm belleği işletim sistemine iade edilemez hale getirebiliyor.

Servisin patlama (burst) düzenli çalışma şekli — Semaphore ile sınırlı eşzamanlı görev sayısı ve iş çalma (work-stealing) zamanlayıcı — bu sorunu daha da belirginleştiriyor. Thread arenaları mmap tabanlı alt-heap'lerle büyüdüğü için, üstteki alt-heap'te konsolide edilmemiş serbest parçalar kaldığında altındaki alt-heap'ler boş olsa bile bellekte tutulmaya devam ediyor. Sonuç olarak her yeni patlama, önceki patlamadan arta kalan serbest blokları yeniden kullanıyor ve RSS önceki zirveyi aşmasa da hiçbir zaman düşmüyor. malloc_trim(0) çağrısı belleği anında temel seviyeye indiriyor, ancak olay tabanlı bir serviste bunu üretimde güvenli ve deterministik şekilde tetiklemek mümkün olmuyor.

Ekip, glibc yerine jemalloc'a (tikv_jemallocator üzerinden Rust'ta global allocator olarak) geçtiğinde, patlama sonrası belleğin gerçekten geri düştüğünü gözlemliyor. Bu durum, Rust mühendisleri için önemli bir hatırlatma: Kubernetes'te gördüğünüz yüksek RSS her zaman bir kod hatası veya sızıntı anlamına gelmiyor; allocator seçimi ve davranışı, özellikle bursty/sparse iş yüklerinde bellek geri kazanımını doğrudan etkiliyor. dhat gibi araçlarla heap ölçümü yapıp glibc'nin arena/top-chunk mekaniğini anlamak, gereksiz kod optimizasyonlarına zaman harcamadan doğru teşhise ulaşmayı sağlıyor.