Finalizer adalah method khusus yang runtime jalankan sebagai upaya terakhir sebelum memori sebuah objek diklaim. Penulisannya memakai tanda tilde di depan nama class. Tujuannya memberi kesempatan melepas resource non-managed apabila pemakai class lupa memanggil Dispose. Kita perlu memahaminya dengan benar, karena finalizer punya keterbatasan serius: waktunya tidak dapat diprediksi.

Sintaks dan Perilaku #

csharp
BuatLog();

// Percobaan mengamati finalizer (hanya untuk eksperimen):
GC.Collect();
GC.WaitForPendingFinalizers();

Console.WriteLine("Method utama selesai");

void BuatLog()
{
    var log = new LogSementara();
    Console.WriteLine("Objek LogSementara dibuat");
}

class LogSementara
{
    ~LogSementara()
    {
        // Waktu eksekusi baris ini TIDAK dapat diprediksi.
        // Runtime memanggilnya pada thread finalizer khusus,
        // setelah GC menemukan objek tanpa rujukan.
        Console.WriteLine("Finalizer LogSementara berjalan");
    }
}

Mari kita telaah bagian pentingnya. Contoh ini tidak menyertakan blok output karena perilaku finalizer bergantung pada waktu, bukan hanya pada kode:

  1. ~LogSementara(): deklarasi finalizer. Hanya class yang boleh memilikinya; struct tidak, dan finalizer tidak boleh menerima parameter atau ditulis manual sebagai method bernama Finalize.
  2. GC.Collect() dan GC.WaitForPendingFinalizers(): dua baris ini membuat eksperimen dapat diamati; GC menjalankan siklus, menemukan objek tanpa rujukan, lalu memasukkannya ke antrean finalizer, dan panggilan WaitForPendingFinalizers menunggu antrean tersebut kosong.
  3. Urutan cetak: tanpa dua baris eksplisit di atas, pesan finalizer bisa muncul belakangan, bahkan sesudah Method utama selesai, atau hanya ketika proses berakhir. Ketidakpastian inilah yang membuat kita tidak boleh meletakkan logika penting di finalizer.

Biaya Tersembunyi Finalizer #

Objek berfinalizer membebani runtime dua kali lipat. Saat GC menemukan objek berfinalizer tanpa rujukan, objek tidak langsung diklaim; GC memindahkannya ke antrean finalizer, sehingga objek selamat minimal satu siklus ekstra dan biasanya naik ke generasi lebih tinggi. Thread finalizer yang sibuk juga menahan seluruh antrean, sehingga finalizer yang lambat memperlambat pengklaiman objek lain.

Karena itu aturannya tegas: finalizer bukan tempat melepas resource managed, dan bukan pengganti Dispose. Bab Finalization pada section Garbage Collection membahas mekanisme runtime di baliknya sekaligus pola Dispose yang benar.