Garbage Collector, otomatik hafıza yönetimi yapabilebilmesini sağlayan programlama dillerin vazgeçilmez bir parçasıdır. .NET, C#, Python, Javascript, Java gibi modern denilebilecek dillerde üretilen uygulamaların hafıza yönetiminin otomatik ve dinamik olarak gerçekleşmesini sağlayan runtime, uygulamanın hafıza isteklerini karşılayabilmek için işletim sisteminden daha önce aldığı hafıza alanından uygulamaya hafıza referansları vermesini sağlar. Uygulamanın bu hafıza referansları ile işi bittiğinde ise bunları geri alır. Bu geri alma işlemini tam olarak GC olarak kısaltılmasını kullanacağımız, Garbage Collector sağlamaktadır.
Türkçe olarak çöp toplayıcı anlamına gelen GC’nin ana amacı; uygulamanın hafıza kaçaklarını engellemek(leak), hafızayı verimli kullanmak(compact) ve hafızada aşmalarını engellemektir. Bu amaçları, uygulamanın elinde tuttuğu hafıza referanslarını adeta tek tek zorlayarak kontrol eder ve artık kullanılmayacağına karar verdiğinde ise onları hafızadan siler.
GC’nin Sağladığı Avantajlar
GC, uygulama hafıza yönetiminin daha verimli gerçekleşmesinin sağlamasının yanı sıra geliştiricinin de vaktini daha iyi değerlendirmesini, uygulama içerisinde yaşanan problemleri daha kısa sürede bulmasını ve aynı zamanda düşük kodlama süresi de sağlamaktadır. Direkt olarak makine kodlarına derlenen programlama dillerinde (Pascal, C, C++) ; hafıza yönetiminin geliştirici tarafından yapılmasına göre, GC kullanımında hafıza kaçaklarının yarattığı problemler daha az meydana çıkmaktadır.
- Uygulama üretimini kolaylaştırır.
- Hata ayıklama(debuug) süresini azaltır.
- Kodlama süresini ve okunabilirliği arttırır.
- Uygulamanın belli bir ölçeğe kadar hafıza problemleriyle daha iyi mücadele etmesini sağlar.
GC’ler Hemen hemen her runtime’da benzer algoritmalara uyarak çalışsa da dillerin doğalarına göre belli farklıklara sahiptirler. Bu yazımızda daha iyi anlaşılabilmesi adına .NET için kullanılan CLR’ın sahip olduğu GC’ye bakış atacağız.
.NET ( Common Language Runtime ) ‘de Garbage Collector
Belki de en basit mantığa sahip olarak CLR tarafından sağlanan GC, uygulamanın kullandığı hafıza referanslarını 3 jenerasyona ayırıyor. GC her çalıştığında objeleri tek tek kontrol ederek, uygulamanın hangi aşamasında hangisinin daha fazla süregeleceğini ölçüyor ve her çalıştığında güncellemek kaydıyla 0. jenerasyonda belli bir noktaya kadar çalışacak olanları, 1.’ye daha uzun ömürlüleri ve 2. jenerasyona da uzun ömürlü olanları yerleştiriyor.
Objelerin en düşük jenerasyonda yer alması ve üst jenerasyona çıkamaması onların çok kısa süreli ömürlerinin olması veya çok sık güncellendiği anlamını taşıyor. En yüksek jenerasyondaki objeler ise diğerlerine nazaran daha uzun ömürlü ve daha az müdahele ihtiyacı duyan hatta güncellenmeyen objjeler olarak belirleniyor. GC’nin böyle bir anlayışla çalışması, onun her seferinde hafızada bulunan referansları kontrol etmesinin önüne geçiyor ve daha hızlı kontrol süreci gerçekleştirmesini sağlıyor.
Hafızanın Birleştirilmesi
Süreç boyunca bir sonraki jenerasyona geçemeyen objeler, işaretlenir ve silinir. Bu silinme sonrası hafızada kısmi boşluklar açılsa da, yine de dağınık bir hafıza kullanımı meydana gelir. Hafızanın daha verimli takibi ve daha tasarruflu kullanımı için hafızadan silinen referanslar sonrası compact edilmesi yani birleştirilmesi gerekiyor.
Thread’ın Durdurulması (GC Pause)
GC, her ne kadar hafızanın optimizasyonu için çalışsa da malesef uygulamanın koştuğu sırada uygulamaya gecikmeler de yaşatabilmektedir. Uygulamanın; belli noktalardan cevap beklediği ve sonuç çıkartması gereken kısımlarda(catch, then, finally) olaya müdahele etmese de, uygulamanın akışında koyduğu belli noktalarda GC’nin çalışması sağlanmaktadır. GC’nin çalışması tam anlamıyla thread’ların da durdurulması anlamını taşımaktadır. Bu yüzden thread durdurulmasını telafi edemeyecek real time uygulamalarda direkt olarak makine diline derlenen yani herhangi bir GC içermeyen programlama dilleri tercih edilmektedir. Çünkü GC’nin yaşatacağı bu gecikmeleri telafi edemez.
Uygulama başlamadan önce GC, uygulamanın çıktı ve birbirine bağlı olmayan belli noktalarına safe pointler işler ve tüm thread’ların bu noktalara gelmesini bekler. Tüm thread’ların işlerini bitirip, bu noktalara gelmesinde yaşadığı gecikmeler ve süreler haliyle GC pause‘nin de ne kadar uzun süreceğini etkilemektedir.
Yazımızın başında bahsettiğimiz gibi GC, runtime ile çalışan programlama dillerinin bir parçası olup; bu dillerin kendine özgü yapılarına göre farklı esnekliklerde ve farklı kurallarda çalışmaktadır. Bu yazımızda diğerlerine göre biraz daha kuş bakışı bir görüntü almamızı sağlayan .NET CLR’ının içerdiği GC üzerinde durduk. Bir sonraki yazımızda ise Javascript’in V8 motorunda kullanılan GC’nin üzerinde detaylı bir incelemeye yapacaığız.
Faydalandığımız içerikler;