Kapan saya harus menggunakan GC.SuppressFinalize ()?

287

Di .NET, dalam kondisi apa saya harus menggunakan GC.SuppressFinalize()?

Apa keuntungan yang diberikan metode ini kepada saya?

GEOCHET
sumber
Saya telah melihat beberapa pertanyaan tentang finalizer dan IDisposable, stackoverflow juga harus memiliki sesuatu tentang GC.SupressFinalize dan referensi lemah
Sam Saffron
Saya tidak berpikir referensi yang lemah melakukan banyak hal tentang finalizer - mungkin Anda harus memposting pertanyaan yang lebih langsung tentang mereka.
Michael Burr
Yerp saya bermaksud memposting pertanyaan terpisah tentang referensi lemah, semua ini dapat mengikat ketika Anda membangun kumpulan objek. Juga saya harus mengajukan pertanyaan tentang objek revival ala ReRegisterForFinalize
Sam Saffron

Jawaban:

296

SuppressFinalizeseharusnya hanya dipanggil oleh kelas yang memiliki finalizer. Ini menginformasikan kepada Pengumpul Sampah (GC) bahwa thisobjek telah dibersihkan sepenuhnya.

IDisposablePola yang disarankan ketika Anda memiliki finalizer adalah:

public class MyClass : IDisposable
{
    private bool disposed = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // called via myClass.Dispose(). 
                // OK to use any private object references
            }
            // Release unmanaged resources.
            // Set large fields to null.                
            disposed = true;
        }
    }

    public void Dispose() // Implement IDisposable
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    ~MyClass() // the finalizer
    {
        Dispose(false);
    }
}

Biasanya, CLR menyimpan tab pada objek dengan finalizer ketika mereka dibuat (membuatnya lebih mahal untuk dibuat). SuppressFinalizememberi tahu GC bahwa objek telah dibersihkan dengan benar dan tidak perlu masuk ke antrian finalizer. Itu terlihat seperti destruktor C ++, tetapi tidak bertindak seperti itu.

The SuppressFinalizeoptimasi tidak sepele, sebagai obyek Anda dapat hidup lama menunggu di antrian finalizer. Jangan tergoda untuk memanggil SuppressFinalizebenda lain, ingatlah Anda. Itu cacat serius menunggu untuk terjadi.

Pedoman desain memberi tahu kami bahwa finalizer tidak diperlukan jika objek Anda diimplementasikan IDisposable, tetapi jika Anda memiliki finalizer, Anda harus mengimplementasikannyaIDisposable untuk memungkinkan pembersihan deterministik kelas Anda.

Sebagian besar waktu Anda harus bisa lolos IDisposable membersihkan sumber daya. Anda hanya perlu finalizer ketika objek Anda memegang sumber daya yang tidak dikelola dan Anda perlu memastikan sumber daya tersebut dibersihkan.

Catatan: Terkadang coders akan menambahkan finalizer ke debug build dari IDisposablekelas mereka sendiri untuk menguji kode yang telah membuang IDisposableobjek mereka dengan benar.

public void Dispose() // Implement IDisposable
{
    Dispose(true);
#if DEBUG
    GC.SuppressFinalize(this);
#endif
}

#if DEBUG
~MyClass() // the finalizer
{
    Dispose(false);
}
#endif
Robert Paulson
sumber
1
Dalam cuplikan kode pertama, saya hanya memposting seperti apa pola IDisposable + finalizer yang disarankan. Kode debug itu bagus, tetapi bisa mengganggu. .. Saya hanya dapat merekomendasikan menghindari finalizer kecuali untuk kelas yang memiliki sumber daya yang tidak dikelola. Menulis kode finalizer aman adalah tidak sepele.
Robert Paulson
1
Hai, Kenapa kita perlu memanggil dispose dengan false sebagai parameter dari finalizer? Bagaimana jika pembuangan tidak pernah dipanggil dan kemudian tidak akan dibuang? Bagaimana jika kita hanya memeriksa apakah objek telah dibuang atau belum dan melakukan pembersihan yang sebenarnya.
Pemimpi
3
@Dreamer - tergantung pada implementasi Anda. Secara umum Anda ingin tahu apakah Buang dipanggil oleh finalizer versus implementasi IDisposable.Dispose (). Jika dipanggil dari finalizer, Anda harus menganggap referensi pribadi tidak lagi valid, dan Anda benar-benar tidak bisa berbuat banyak. Namun jika dipanggil dari IDisposable.Dispose (), Anda tahu bahwa referensi masih valid.
Robert Paulson
32
Jika implementasi kelas IDisposabletidak sealed, maka harus menyertakan panggilan GC.SuppressFinalize(this) bahkan jika itu tidak termasuk finalizer yang ditentukan pengguna . Ini diperlukan untuk memastikan semantik yang tepat untuk tipe turunan yang menambahkan finalizer yang ditentukan pengguna tetapi hanya menimpa Dispose(bool)metode yang dilindungi .
Sam Harwell
1
Tidak sealedseperti yang disebutkan oleh @SamHarwell adalah penting, untuk kelas turunan. Hasil CodeAnalysis di ca1816 + ca1063 ketika kelas tidak disegel, tetapi kelas disegel baik-baik saja tanpa SuppressFinalize.
dashesy
38

SupressFinalizememberitahu sistem bahwa pekerjaan apa pun yang telah dilakukan di finalizer telah dilakukan, sehingga finalizer tidak perlu dipanggil. Dari .NET docs:

Objek yang mengimplementasikan antarmuka IDisposable dapat memanggil metode ini dari metode IDisposable.Dispose untuk mencegah pengumpul sampah memanggil Object.Finalize pada objek yang tidak memerlukannya.

Secara umum, hampir semua Dispose() metode harus dapat memanggil GC.SupressFinalize(), karena itu harus membersihkan segala sesuatu yang akan dibersihkan di finalizer.

SupressFinalizehanyalah sesuatu yang menyediakan optimasi yang memungkinkan sistem untuk tidak repot mengantri objek ke thread finalizer. Seorang Dispose()finalizer yang ditulis dengan benar harus bekerja dengan baik dengan atau tanpa panggilan GC.SupressFinalize().

Michael Burr
sumber
2

Metode itu harus dipanggil pada Disposemetode objek yang mengimplementasikan IDisposable, dengan cara ini GC tidak akan memanggil finalizer lain kali jika seseorang memanggilDispose metode tersebut.

Lihat: Metode GC.SuppressFinalize (Obyek) - Microsoft Documents

albertein
sumber
9
Saya pikir "Harus" salah - bahkan "harus" - Hanya saja dalam beberapa skenario, Anda dapat menghilangkan overhead antrian / menyelesaikan objek.
Dasar
1
Dispose(true);
GC.SuppressFinalize(this);

Jika objek memiliki finalizer, .net menaruh referensi dalam antrian finalisasi.

Karena kita memiliki panggilan Dispose(ture), itu jelas objek, jadi kita tidak perlu antrian finalisasi untuk melakukan pekerjaan ini.

Jadi panggilan GC.SuppressFinalize(this)hapus referensi dalam antrian finalisasi.

Max CHien
sumber
0

Jika suatu kelas, atau sesuatu yang diturunkan darinya, mungkin memiliki referensi langsung terakhir ke objek dengan finalizer, maka salah satu GC.SuppressFinalize(this)atauGC.KeepAlive(this) harus dipanggil pada objek setelah operasi apa pun yang mungkin terpengaruh oleh finalizer itu, sehingga memastikan bahwa finalizer menang. dapat berjalan sampai setelah operasi itu selesai.

Biaya GC.KeepAlive()dan GC.SuppressFinalize(this)pada dasarnya sama di setiap kelas yang tidak memiliki finalizer, dan kelas yang memiliki finalizer umumnya harus memanggil GC.SuppressFinalize(this), jadi menggunakan fungsi yang terakhir sebagai langkah terakhir Dispose()mungkin tidak selalu diperlukan, tetapi tidak akan diperlukan. salah

supercat
sumber