C# Memory Management ve Garbage Collector
C#’ta memory management ve garbage collector yapısını öğrenin. Bellek yaşam döngüsü, tahsis ve temizlik süreçleri anlatılıyor.
.NET uygulamalarında bellek yönetimi (Memory Management) otomatik olarak Garbage Collector (GC) tarafından yapılır. Geliştirici, nesnelerin yaşam döngüsünü manuel olarak yönetmek zorunda değildir. GC, kullanılmayan nesneleri tespit eder ve bellekten temizler. Ancak bellek yönetiminin nasıl çalıştığını anlamak, performans optimizasyonu açısından büyük önem taşır.
Bellek Yönetimi Nedir?
Bellek yönetimi, uygulamanın RAM üzerinde oluşturduğu nesnelerin (objelerin) yaşam döngüsünü takip etme sürecidir. .NET ortamında iki tür bellek alanı bulunur:
- Stack: Değer tipleri (
int,bool,structvb.) için kullanılır. Hızlıdır ve otomatik serbest bırakılır. - Heap: Referans tipleri (
class,string,arrayvb.) için kullanılır. GC tarafından yönetilir.
Stack ve Heap Arasındaki Fark
| Özellik | Stack | Heap |
|---|---|---|
| Bellek Alanı | Küçük ve hızlı | Büyük, yönetilen alan |
| Depolanan Türler | Değer tipleri | Referans tipleri |
| Yönetim | Otomatik (scope bazlı) | Garbage Collector tarafından yönetilir |
| Yaşam Döngüsü | Metot bittiğinde temizlenir | GC tarafından tespit edilene kadar kalır |
| Erişim Hızı | Çok hızlı | Daha yavaş |
Garbage Collector (GC) Nedir?
Garbage Collector, heap üzerindeki kullanılmayan nesneleri otomatik olarak temizleyen bileşendir. Bu sayede memory leak (bellek sızıntısı) riski azalır. GC, programın çalışması sırasında belirli aralıklarla devreye girer ve artık referans edilmeyen nesneleri serbest bırakır.
using System;
class Program
{
static void Main()
{
for (int i = 0; i < 1000; i++)
{
var data = new byte[1024 * 1024]; // 1 MB
}
Console.WriteLine("Bellek temizlenmeden önce...");
GC.Collect(); // Elle GC çağrısı
Console.WriteLine("Bellek temizlendikten sonra...");
}
}
GC’nin elle çağrılması genellikle önerilmez, yalnızca özel senaryolarda (ör. test veya bellek yoğun işlemler sonrası) yapılmalıdır.
GC Nasıl Çalışır?
Garbage Collector, “generation-based collection” (nesil tabanlı toplama) mantığıyla çalışır. Bu, sık oluşturulup kısa ömürlü nesnelerle uzun ömürlü nesnelerin farklı alanlarda tutulması anlamına gelir.
- Gen 0 (Generation 0): Yeni oluşturulan nesneler. En sık temizlenen alan.
- Gen 1: Gen 0’dan sağ kalan nesneler. Orta ömürlü nesneler için kullanılır.
- Gen 2: Uzun süre yaşayan nesneler (ör. Singleton, cache, static veriler).
GC yalnızca kısa ömürlü nesneleri değil, tüm heap alanını tarayarak kök referanslardan ulaşılmayan nesneleri serbest bırakır.
Dispose ve Finalize Farkı
Bellek yönetiminde yalnızca GC yeterli değildir.
Unmanaged kaynaklar (ör. dosya, ağ bağlantısı, GDI nesneleri) GC tarafından otomatik temizlenmez.
Bu tür nesneler için IDisposable arayüzü ve Finalize (yıkıcı metot) kullanılmalıdır.
class DosyaKaynak : IDisposable
{
private bool disposed = false;
public void Yaz(string veri)
{
if (disposed)
throw new ObjectDisposedException(nameof(DosyaKaynak));
Console.WriteLine($"Veri yazılıyor: {veri}");
}
public void Dispose()
{
if (!disposed)
{
Console.WriteLine("Kaynak serbest bırakıldı (Dispose).");
disposed = true;
GC.SuppressFinalize(this);
}
}
~DosyaKaynak()
{
Console.WriteLine("Finalize çağrıldı.");
}
}
// Kullanımı:
using (var dosya = new DosyaKaynak())
{
dosya.Yaz("Deneme");
}
Dispose() metodu manuel temizleme için kullanılırken,
Finalize (destructor) GC tarafından çağrılır.
SuppressFinalize() çağrısı, GC’nin tekrar yıkıcı çağırmasını engeller.
Memory Leak (Bellek Sızıntısı) Nedir?
Garbage Collector otomatik olsa da, bazı durumlarda geliştirici kaynakları yanlış yönetirse bellek sızıntısı oluşabilir. Özellikle büyük event handler veya static referans’lar doğru temizlenmezse, nesneler bellekten atılamaz.
// Klasik memory leak örneği:
class Islem
{
public event EventHandler VeriHazir;
}
class Program
{
static Islem i = new Islem();
static void Main()
{
i.VeriHazir += (s, e) => Console.WriteLine("Event bağlı kaldı!");
i = null; // Event handler hâlâ referans tuttuğu için GC temizleyemez!
}
}
Bu durumda event’ler manuel olarak - = (unsubscribe) edilmelidir.
GC Modları ve Performans Ayarları
.NET GC iki ana çalışma moduna sahiptir:
- Workstation GC: Masaüstü uygulamaları için optimize edilmiştir (tek kullanıcı, UI öncelikli).
- Server GC: Çok çekirdekli sunucularda paralel bellek toplama için optimize edilmiştir.
// app.config veya runtimeconfig.json örneği:
<configuration>
<runtime>
<gcServer enabled="true"/>
</runtime>
</configuration>
GC.TryStartNoGCRegion() metodu, belirli bir süre boyunca GC’nin devreye girmesini engelleyebilir.
Bu, gerçek zamanlı (real-time) uygulamalarda kesintisiz çalışma sağlar.
GC Olaylarını İzleme
GC’nin ne zaman çalıştığını veya ne kadar bellek topladığını izlemek için GCNotification ve GC.GetTotalMemory() gibi metotlar kullanılabilir.
using System;
class Program
{
static void Main()
{
long once = GC.GetTotalMemory(false);
var data = new byte[10_000_000];
long sonra = GC.GetTotalMemory(false);
Console.WriteLine($"Fark: {(sonra - once) / 1024 / 1024} MB");
GC.RegisterForFullGCNotification(10, 10);
GC.Collect();
Console.WriteLine("GC tetiklendi!");
}
}
Örnek: Büyük Veri Analizi
Büyük verilerle çalışan uygulamalarda gereksiz nesne oluşturmak GC yükünü artırır.
Aşağıdaki örnek, bellek optimizasyonu için ArrayPool<T> kullanarak nesne yeniden kullanımını gösterir.
using System;
using System.Buffers;
class Program
{
static void Main()
{
var pool = ArrayPool<byte>.Shared;
byte[] buffer = pool.Rent(1024 * 1024); // 1MB buffer kirala
for (int i = 0; i < buffer.Length; i++)
buffer[i] = 255;
Console.WriteLine("Veri işlendi.");
pool.Return(buffer); // GC yükü oluşturmaz
}
}
ArrayPool, sık kullanılan nesnelerin yeniden kullanımını sağlar ve GC baskısını azaltır.
Performans ve Dikkat Edilecekler
- GC’yi manuel çağırmaktan kaçının; .NET bunu kendi zamanlamasıyla yapar.
- Unmanaged kaynakları mutlaka
Disposeveyausingile temizleyin. - Büyük nesne tahsislerinden (
>85KB) kaçının, Large Object Heap (LOH) yavaş temizlenir. - Event’leri iptal etmeyi (
- =) unutmayın; aksi halde GC onları toplayamaz. - Tekrarlayan küçük nesne oluşturma yerine nesne havuzlarını (
ArrayPool,ObjectPool) kullanın.
TL;DR
- Garbage Collector, heap üzerindeki kullanılmayan nesneleri otomatik temizler.
- Stack hızlı, kısa ömürlü değer tiplerini tutar; heap referans tipleri barındırır.
- GC, generation modeline göre (0, 1, 2) çalışır.
- IDisposable unmanaged kaynaklar için manuel temizlik sağlar.
- Memory leak’ler genellikle event veya static referanslardan kaynaklanır.
- Performans için nesne havuzları (
ArrayPool,ObjectPool) kullanılabilir.
İlişkili Makaleler
C# IDisposable ve Using Pattern
C#’ta IDisposable ve using pattern kullanımını öğrenin. Kaynak yönetimi, bellek temizliği ve güvenli nesne yaşam döngüsü anlatılıyor.
C# Span<T> ve Memory<T> ile Performans Optimizasyonu
C#’ta Span<T> ve Memory<T> ile performans optimizasyonunu öğrenin. Bellek yönetimi ve yüksek performanslı veri işlemleri örneklerle.
C# Unsafe Kod ve Pointer Kullanımı
C#’ta unsafe kod ve pointer kullanımını öğrenin. Bellek adresleri, pointer işlemleri ve düşük seviye senaryolar örneklerle anlatılıyor.