Penting untuk memisahkan pembuangan dari pengumpulan sampah. Mereka benar-benar hal yang terpisah, dengan satu kesamaan yang akan saya bahas sebentar lagi.
Dispose
, pengumpulan dan penyelesaian sampah
Saat Anda menulis using
pernyataan, itu hanyalah gula sintaksis untuk blok coba / akhirnya sehingga yang Dispose
dipanggil bahkan jika kode di badan using
pernyataan itu mengeluarkan pengecualian. Ini tidak berarti bahwa objek tersebut adalah sampah yang dikumpulkan di akhir blok.
Pembuangan adalah tentang sumber daya yang tidak dikelola ( sumber daya non-memori). Ini bisa berupa pegangan UI, koneksi jaringan, pegangan file, dll. Ini adalah sumber daya yang terbatas, jadi Anda biasanya ingin merilisnya secepat mungkin. Anda harus mengimplementasikan IDisposable
setiap kali tipe Anda "memiliki" sumber daya yang tidak dikelola, baik secara langsung (biasanya melalui IntPtr
) atau tidak langsung (misalnya melalui a Stream
, a SqlConnection
dll).
Pengumpulan sampah itu sendiri hanya tentang memori - dengan satu putaran kecil. Pengumpul sampah dapat menemukan objek yang tidak lagi dapat dirujuk, dan membebaskannya. Ia tidak mencari sampah sepanjang waktu - hanya ketika mendeteksi bahwa ia perlu (misalnya jika satu "generasi" dari heap kehabisan memori).
Perubahan itu adalah finalisasi . Pengumpul sampah menyimpan daftar objek yang tidak lagi dapat dijangkau, tetapi memiliki finalizer (ditulis seperti ~Foo()
dalam C #, agak membingungkan - tidak seperti C ++ destructors). Ini menjalankan finalizer pada objek-objek ini, kalau-kalau mereka perlu melakukan pembersihan ekstra sebelum memori mereka dibebaskan.
Finalizer hampir selalu digunakan untuk membersihkan sumber daya jika pengguna jenis tersebut lupa membuangnya secara tertib. Jadi jika Anda membuka FileStream
tetapi lupa untuk memanggil Dispose
atau Close
, finalizer pada akhirnya akan melepaskan pegangan file yang mendasarinya untuk Anda. Dalam program yang ditulis dengan baik, finalisator seharusnya tidak pernah menyala menurut saya.
Menetapkan variabel ke null
Satu hal kecil tentang menyetel variabel ke null
- ini hampir tidak pernah diperlukan demi pengumpulan sampah. Terkadang Anda mungkin ingin melakukannya jika itu adalah variabel anggota, meskipun menurut pengalaman saya jarang "bagian" dari suatu objek tidak lagi diperlukan. Ketika itu adalah variabel lokal, JIT biasanya cukup pintar (dalam mode rilis) untuk mengetahui kapan Anda tidak akan menggunakan referensi lagi. Sebagai contoh:
StringBuilder sb = new StringBuilder();
sb.Append("Foo");
string x = sb.ToString();
// The string and StringBuilder are already eligible
// for garbage collection here!
int y = 10;
DoSomething(y);
// These aren't helping at all!
x = null;
sb = null;
// Assume that x and sb aren't used here
Satu waktu di mana mungkin layak untuk mengatur variabel lokal null
adalah ketika Anda berada dalam loop, dan beberapa cabang dari loop perlu menggunakan variabel tetapi Anda tahu Anda telah mencapai titik di mana Anda tidak melakukannya. Sebagai contoh:
SomeObject foo = new SomeObject();
for (int i=0; i < 100000; i++)
{
if (i == 5)
{
foo.DoSomething();
// We're not going to need it again, but the JIT
// wouldn't spot that
foo = null;
}
else
{
// Some other code
}
}
Menerapkan IDisposable / finalizers
Jadi, haruskah tipe Anda menerapkan finalizer? Hampir pasti tidak. Jika Anda hanya memiliki sumber daya yang tidak dikelola secara tidak langsung (misalnya Anda memiliki FileStream
variabel sebagai anggota), maka menambahkan finalizer Anda sendiri tidak akan membantu: aliran hampir pasti memenuhi syarat untuk pengumpulan sampah saat objek Anda, jadi Anda dapat mengandalkan FileStream
memiliki finalizer (jika perlu - ini mungkin merujuk ke sesuatu yang lain, dll). Jika Anda ingin memiliki sumber daya yang tidak dikelola "hampir" secara langsung, SafeHandle
itu teman Anda - dibutuhkan sedikit waktu untuk memulai, tetapi itu berarti Anda hampir tidak perlu menulis finalizer lagi . Anda biasanya hanya memerlukan finalizer jika Anda memiliki pegangan yang benar-benar langsung pada sumber daya (an IntPtr
) dan Anda harus melihat untuk pindah keSafeHandle
secepat yang kamu bisa. (Ada dua tautan di sana - baca keduanya, idealnya.)
Joe Duffy memiliki seperangkat pedoman yang sangat panjang seputar finalisator dan IDisposable (ditulis bersama dengan banyak orang pintar) yang layak dibaca. Perlu diketahui bahwa jika Anda menyegel kelas Anda, itu membuat hidup jauh lebih mudah: pola penimpaan Dispose
untuk memanggil Dispose(bool)
metode virtual baru dll hanya relevan ketika kelas Anda dirancang untuk warisan.
Ini memang sedikit bertele-tele, tapi tolong tanyakan klarifikasi di mana Anda ingin :)
Saat Anda membuang suatu objek, sumber daya dibebaskan. Saat Anda menetapkan null ke variabel, Anda hanya mengubah referensi.
Setelah Anda menjalankan ini, objek yang dirujuk oleh myclass masih ada, dan akan berlanjut hingga GC membersihkannya. Jika Buang secara eksplisit dipanggil, atau berada dalam blok penggunaan, sumber daya apa pun akan dibebaskan secepat mungkin.
sumber
Kedua operasi tersebut tidak banyak berhubungan satu sama lain. Saat Anda menyetel referensi ke null, itu akan melakukannya. Itu sendiri tidak mempengaruhi kelas yang dirujuk sama sekali. Variabel Anda tidak lagi menunjuk ke objek yang dulu, tetapi objek itu sendiri tidak berubah.
Saat Anda memanggil Dispose (), itu adalah panggilan metode pada objek itu sendiri. Apapun yang dilakukan metode Buang, sekarang sudah selesai pada objek. Tetapi ini tidak memengaruhi referensi Anda ke objek tersebut.
Satu-satunya area yang tumpang tindih adalah ketika tidak ada lagi referensi ke suatu objek, pada akhirnya akan mengumpulkan sampah. Dan jika kelas mengimplementasikan antarmuka IDisposable, maka Dispose () akan dipanggil pada objek sebelum sampah dikumpulkan.
Tapi itu tidak akan terjadi segera setelah Anda menyetel referensi ke null, karena dua alasan. Pertama, referensi lain mungkin ada, sehingga tidak akan mengumpulkan sampah sama sekali, dan kedua, meskipun itu adalah referensi terakhir, sehingga sekarang siap untuk pengumpulan sampah, tidak akan terjadi apa-apa hingga pengumpul sampah memutuskan untuk menghapus objek.
Memanggil Dispose () pada objek tidak "membunuh" objek dengan cara apa pun. Ini biasanya digunakan untuk membersihkan sehingga objek dapat dihapus dengan aman setelahnya, tetapi pada akhirnya, tidak ada yang ajaib tentang Buang, ini hanya metode kelas.
sumber