Bab Finalizer di section Memory Management melihat finalizer dari sisi pemakaian API; bab ini membongkar mekanismenya dan menetapkan aturan pola Dispose. Finalization adalah proses dua tahap: saat GC menemukan objek berfinalizer yang tidak dicakup rujukan lagi, GC tidak langsung mengklaim memorinya. Runtime memindahkan entri objek ke antrean siap-eksekusi (F-Reachable queue), thread finalizer menjalankan method Finalize, dan barulah pada siklus GC berikutnya memori objek benar-benar diklaim.
Contoh: Membuktikan Pengaruh SuppressFinalize #
var denganDispose = new LogAktivitas(akanDiDispose: true);
denganDispose.Dispose();
BuatTanpaDispose();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Console.WriteLine($"Finalizer setelah Dispose: {LogAktivitas.DenganDispose}");
Console.WriteLine($"Finalizer tanpa Dispose : {LogAktivitas.TanpaDispose}");
void BuatTanpaDispose()
{
var log = new LogAktivitas(akanDiDispose: false);
_ = log;
}
class LogAktivitas(bool akanDiDispose) : IDisposable
{
public static bool DenganDispose { get; private set; }
public static bool TanpaDispose { get; private set; }
private readonly bool _akanDiDispose = akanDiDispose;
~LogAktivitas()
{
if (_akanDiDispose)
DenganDispose = true;
else
TanpaDispose = true;
}
public void Dispose() => GC.SuppressFinalize(this);
}Output yang Dihasilkan:
Finalizer setelah Dispose: False
Finalizer tanpa Dispose : TrueMari kita bedah baris demi baris:
denganDispose.Dispose():DisposememanggilGC.SuppressFinalize(this), yaitu memerintahkan runtime untuk tidak memasukkan objek ke antrean finalizer.BuatTanpaDispose(): objek kedua dibuat tanpaDispose; ketika method kembali tidak ada lagi rujukan kuat ke objek tersebut.GC.WaitForPendingFinalizers(): menunggu seluruh finalizer yang sudah masuk antrean selesai dieksekusi; thread finalizer lalu menandaiTanpaDisposemenjadiTrue.- Dua baris laporan:
Falsemembuktikan finalizer pada objek yang di-Dispose tidak pernah berjalan, danTruemembuktikan finalizer tetap berjalan untuk objek yang tidak di-Dispose.
Aturan Pola Dispose #
- Class dengan finalizer sebaiknya
sealed, atau polaDispose(bool)dipakai agar class turunan ikut tertata. Disposewajib memanggilGC.SuppressFinalize(this)supaya objek tidak selamat dua siklus GC tanpa guna.- Badan finalizer hanya menyentuh resource non-managed; melepas objek managed di finalizer justru menunda pengklaimannya.
- Panggil
Disposelebih dulu; finalizer hanyalah jaring pengaman, bukan jalur utama.
Urutan eksekusi antar finalizer tidak dijamin. Runtime memasukkan entri ke antrean sesuai urutan penemuan saat penelusuran, bukan urutan pembuatan objek, sehingga finalizer tidak boleh menyentuh objek managed lain yang juga berfinalizer. Objek tersebut mungkin telah dieksekusi lebih dulu, dan mengasumsikan kondisinya berarti mengandalkan perilaku yang tidak pernah dijanjikan runtime.