Objek di managed heap tidak dibebaskan pada saat tertentu yang bisa kita tentukan; usianya mengikuti rujukan yang masih terpakai. Selama ada satu variabel, field, atau elemen koleksi yang masih menunjuk objek, GC membiarkan objek tersebut tetap ada. Begitu seluruh rujukan hilang, objek menjadi kandidat pengklaiman pada siklus GC berikutnya. Konsep ini disebut reference lifetime, dan kita akan membuktikannya dengan WeakReference, yaitu rujukan khusus yang tidak menahan objek.

Contoh: Dua Fase Usia Objek #

csharp
TunjukkanSelamatSaatDirujuk();
TunjukkanDiklaimTanpaRujukan();

void TunjukkanSelamatSaatDirujuk()
{
    byte[] kunci = new byte[1_000];
    var jejak = new WeakReference(kunci);

    GC.Collect();
    Console.WriteLine($"Selama dirujuk : {jejak.IsAlive}");

    GC.KeepAlive(kunci);
}

void TunjukkanDiklaimTanpaRujukan()
{
    var jejak = BuatJejak();

    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect();
    Console.WriteLine($"Setelah dilepas: {jejak.IsAlive}");
}

WeakReference BuatJejak()
{
    byte[] kunci = new byte[1_000];
    return new WeakReference(kunci);
}

Output yang Dihasilkan:

text
Selama dirujuk : True
Setelah dilepas: False

Mari kita bedah baris demi baris:

  1. new WeakReference(kunci): kita menyimpan rujukan lemah ke array. Rujukan lemah tidak menjaga objek tetap ada, tetapi masih bisa mengintip kondisinya.
  2. GC.Collect() pada fase pertama: GC berjalan, tetapi variabel kunci masih menunjuk array, sehingga array lolos dan IsAlive bernilai True. Pemanggilan GC.KeepAlive(kunci) memastikan compiler tidak menganggap variabel tersebut selesai lebih awal.
  3. BuatJejak() pada fase kedua: array dibuat di dalam method terpisah. Ketika method tersebut kembali, tidak ada lagi rujukan kuat yang tersisa ke array.
  4. GC.Collect() pada fase kedua: GC tidak menemukan rujukan kuat, sehingga array diklaim kembali dan IsAlive berubah menjadi False.

Rujukan yang Tidak Sengaja Dipertahankan #

Kebocoran memori di aplikasi .NET jarang berupa objek yang tak terdeteksi; objeknya justru masih dirujuk, hanya saja tanpa disadari. Penyebab yang paling sering: event yang berlangganan tetapi tidak dilepas, field static yang terus memegang data lama, dan closure yang menangkap variabel besar. Bab Event Memory Leaks pada section Delegates dan Events membahas kasus event secara khusus.