Garbage Collector nasıl çalışır?

Bu flood’da .net CLR - Common Language Runtime'daki Garbage Collector üzerinden çöp toplayıcıların nasıl çalıştığını kısaca anlatacağım.

C/C++/Pascal gibi dillerle yazılan uygulamalar direkt olarak makine koduna dönüştürülerek çalıştırılır. .NET, Java, Python gibi dillerde yazılan uygulamalar ise bir runtime tarafından çalıştırılırlar. runtime'a sahip olmanın en önemli avantajlarından biri uygulamanın hafıza yönetimini runtime'ın otomatik olarak yapmasıdır. runtime uygulamanın hafıza isteklerini işletim sisteminden önceden aldığı hafıza alanından karşılar, uygulamanın hafıza alanı ile işi bittiğinde ise geri alır. Hafızanın geri alınması görevini Garbage Collector - GC yapar.

GC otomatik hafıza yönetimi ile uygulama yazımını kolaylaştırmakta, kodlama süresini kısaltmaktadır. Yine GC, makine koduna derlenen dillere göre kodlama hatalarından dolayı karşılaşılan hafıza alanlarının leak edilmesi problemi ile de bir seviyeye kadar baş edebilmektedir.

Şimdi GC'nin CLR'da nasıl uygulandığını inceleyelim. CLR uygulamalara sağladığı hafızayı 3 jenerasyona ayırır. Jenerasyon 0, 1 ve 2. Yeni oluşturulan nesneler ilk olarak jenerasyon 0'a atılır. GC koşarak uygulamada artık referansı kalmayan objeleri temizler. Jenerasyon 0'da bulunan objelerden kullanılmaya devam edenler jenerasyon 1'e taşınır, jenerasyon 1'de bulunan ve kullanılmaya devam eden objeler ise jenerasyon 2'ye taşınır. Bir objenin jenerasyon 2'de olması demek objenin uygulama tarafından uzun süre referans edilmesi demektir.

Jenerasyon arası geçişler aşağıdaki şekilde gösterilmiştir. GC koştuğunda uygulamada referansı kalmayan ve temizlenen objeler bulanıklaştırılmış diğer objeler ise bir sonraki jenerasyona geçirilmişlerdir.

Hafızanın üç jenerasyona bölünmesinin sebebi GC performansını artırmaktır. Yapılan jenerasyon sınıflandırması ile GC'nin her seferinde bütün objelerin referanslarını kontrol etmesi gerekmez ve çöp toplama süreci daha hızlı bir şekilde tamamlanabilir.

GC çalıştığında uygulamada artık referans edilmeyen objeleri temizler, hafıza fragmantasyonunu engellemek için de temizlikle boşalan alanları hala kullanılmakta olan objeleri taşımak suretiyle birleştirir. Hafıza fragmantasyonunun önemi aşağıdaki şekilde gösterilmiştir.

Hafıza alanlarının fragmantasyonu GC ile önlenir ve uygulamanın büyük objeleri tutmak için ihtiyaç duyacağı hafıza alanları yaratılmış olur. Fragmante durumda ise hafızada yeterli toplam alan olsa bile büyük objeleri koymak üzere uygun hafıza alanı bulunamayabilir.

GC hafıza alanlarının yerlerini değiştirme gibi tehlikeli işlemler yaptığı için GC yapılan Thread koşarken uygulamadaki diğer bütün Thread'lerin geçici olarak durdurulması gerekmektedir. Buna GC Pause denir ve sistem GC sürecinde işlevini gerçekleştiremez.

Birçok uygulama GC'nin sebep olduğu bu küçük (10-200 ms) duraklamaları fonksiyon kaybı olmadan ve kullanıcı fark etmeden tolere edebilecek durumdadır. Real Time (gerçek zamanlı) sistemler bu kapsamda değildir. Bu sebeple Real Time sistemler GC olan dillerde yazılamazlar.

GC koşarken uygulamadaki bütün Thread'lerin durdurulur fakat Thread'ler GC için herhangi bir anda durdurulamaz. Thread'ler ancak ve ancak GC çalışmasına uygun Safe Point'lerde durdurulabilirler. Safe Point'ler Thread'lerin herhangi bir mutex almadığı, catch, finally gibi bloklarda olmadığı yerlerdir. CLR uygulamayı çalıştırırken kodun uygun yerlerine Safe Point sıçrama noktaları enjekte eder. GC başlayacağı zaman öncelikle Thread'lere durdurma isteği gönderilir, bütün Thread'ler Safe Point'lere gelene kadar beklenir.

Anlaşılacağı üzere bazı özel durumlarda ilgili Thread'in hata işlemesi veya mutex tutması çok uzun sürüyorsa bütün Thread'lerin Safe Point'e ulaşması daha uzun süreceği için GC Pause olması gerekenden daha uzun sürer ve yüklü bir sistem performans kaybı yaşar.

Son olarak, performansın çok kritik olduğu sistemlerde CLR'dan GC'nin devreye girmek üzere olduğu ile ilgili bildirim alınarak sistemin yükü hafifletilerek kullanıcıların duraklamadan etkilenmemesi sağlanabilir. Bu senaryo biraz ekstrem gelebilir ancak milisaniyelerin bile önemli olduğu finansal (özellikle borsa, döviz, vb) işlemlerde bu tarz detaylara bile dikkat edilmelidir.

İlerleyen flood’larda GC performansını artırmak için kullanılabilecek yöntemlere göz atacağız.