Memeriksa finalizer kelas
Finalizer (secara historis disebut sebagai destruktor) digunakan untuk melakukan pembersihan akhir yang diperlukan ketika instans kelas dikumpulkan oleh pengumpul sampah (GC). Dalam kebanyakan kasus, Anda dapat menghindari penulisan finalizer dengan menggunakan kelas System.Runtime.InteropServices.SafeHandle atau turunan untuk membungkus handel yang tidak dikelola.
Beberapa hal yang perlu diingat saat menggunakan finalizer:
- Finalizer tidak dapat ditentukan dalam jenis
struct. Mereka hanya digunakan dengan kelas. - Kelas hanya dapat memiliki satu finalizer.
- Finalizer tidak dapat diwariskan atau kelebihan beban.
- Finalizer tidak dapat dipanggil. Mereka dipanggil secara otomatis.
- Finalizer tidak mengambil pengubah atau memiliki parameter.
Sintaksis finalizer
Di C#, finalizer didefinisikan menggunakan tilde (~) diikuti dengan nama kelas. Ini tidak mengambil parameter apa pun dan tidak dapat dipanggil secara eksplisit.
class Car
{
~Car() // finalizer
{
// cleanup statements...
}
}
Finalizer juga dapat diimplementasikan sebagai definisi isi ekspresi, seperti yang ditunjukkan contoh berikut.
public class Destroyer
{
public override string ToString() => GetType().Name;
~Destroyer() => Console.WriteLine($"The {ToString()} finalizer is executing.");
}
Hubungan antara pengumpulan sampah dan finalizer
Pengumpul sampah di .NET secara otomatis mengelola alokasi dan pelepasan memori untuk objek terkelola. Ketika objek tidak lagi direferensikan, GC menandainya untuk koleksi dan akhirnya mengklaim kembali memorinya. Jika kelas memiliki finalizer, GC memanggil finalizer sebelum mengklaim ulang memori objek. Finalizer memungkinkan objek untuk merilis sumber daya yang tidak dikelola yang dipegangnya.
Proses finalisasi biasanya melibatkan langkah-langkah berikut:
- Ketika GC mendeteksi bahwa objek dengan finalizer tidak lagi dapat dijangkau, GC memindahkan objek ke antrean finalisasi.
- Utas finalizer menjalankan metode finalizer.
- Setelah finalizer berjalan, objek dipindahkan ke antrean
freachableGC, di mana memenuhi syarat untuk pengumpulan sampah di siklus GC berikutnya.
Contoh finalizer
public class ResourceHolder
{
// Unmanaged resource
private IntPtr unmanagedResource;
// Constructor
public ResourceHolder()
{
// Allocate unmanaged resource
unmanagedResource = /* allocate resource */;
}
// Finalizer
~ResourceHolder()
{
// Release unmanaged resource
if (unmanagedResource != IntPtr.Zero)
{
// Free the resource
/* free resource */
unmanagedResource = IntPtr.Zero;
}
}
}
Menerapkan pola pembuangan dengan finalizer
Antarmuka seperti IDisposable dan IAsyncDisposable digunakan untuk merilis sumber daya secara deterministik. Metode Dispose dipanggil secara eksplisit untuk merilis sumber daya, sementara finalizer bertindak sebagai jaring pengaman untuk memastikan bahwa sumber daya dirilis bahkan jika Dispose tidak dipanggil.
Penting
Membuat dan menggunakan antarmuka berada di luar cakupan modul ini. Penggunaan IDisposable dijelaskan di sini untuk memberikan konteks untuk pola "buang". Pelatihan yang menyediakan pengenalan antarmuka tersedia di platform Microsoft Learn.
Ingatlah poin-poin berikut saat menggunakan finalizer:
- Finalizer bersifat nondeterministik, yang berarti Anda tidak dapat memprediksi kapan finalizer berjalan. Tergantung pada jadwal GC. Perilaku ini dapat menyebabkan keterlambatan dalam merilis sumber daya yang tidak dikelola.
- Finalizer dapat memengaruhi performa karena objek dengan finalizer membutuhkan waktu lebih lama untuk dikumpulkan.
- Menerapkan antarmuka
IDisposabledan metodeDisposeuntuk pembersihan sumber daya deterministik disarankan. MetodeDisposedapat dipanggil secara eksplisit untuk melepaskan sumber daya, dan finalizer dapat digunakan sebagai jaring pengaman.
Berikut adalah contoh kelas yang mengimplementasikan antarmuka IDisposable dan finalizer:
public class ResourceHolder : IDisposable
{
// Unmanaged resource
private IntPtr unmanagedResource;
private bool disposed = false;
// Constructor
public ResourceHolder()
{
// Allocate unmanaged resource
unmanagedResource = /* allocate resource */;
}
// Dispose method
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// Free other managed objects
}
// Free unmanaged resources
if (unmanagedResource != IntPtr.Zero)
{
/* free resource */
unmanagedResource = IntPtr.Zero;
}
disposed = true;
}
}
// Finalizer
~ResourceHolder()
{
Dispose(false);
}
}
Dalam contoh ini:
- Metode
Disposedipanggil untuk merilis sumber daya secara deterministik. - Finalizer memanggil
Dispose(false)untuk merilis sumber daya yang tidak dikelola jikaDisposetidak dipanggil. -
GC.SuppressFinalize(this)digunakan untuk mencegah finalizer berjalan jikaDisposetelah dipanggil.