Dalam pengalaman saya, tampaknya kebanyakan orang akan memberi tahu Anda bahwa tidak bijaksana untuk memaksa pengumpulan sampah tetapi dalam beberapa kasus di mana Anda bekerja dengan objek besar yang tidak selalu terkumpul dalam generasi 0 tetapi di mana memori menjadi masalah, adalah apakah boleh memaksa pengumpulan? Apakah ada praktik terbaik untuk melakukannya?
sumber
TargetOfInvocationNullException
.Anggap saja seperti ini - apakah lebih efisien membuang sampah dapur jika tempat sampah sudah 10% atau dibiarkan terisi sebelum dibuang?
Dengan tidak membiarkannya terisi, Anda membuang-buang waktu berjalan ke dan dari tempat sampah di luar. Ini serupa dengan apa yang terjadi ketika utas GC berjalan - semua utas yang dikelola ditangguhkan saat sedang berjalan. Dan Jika saya tidak salah, utas GC dapat dibagikan di antara beberapa AppDomain, jadi pengumpulan sampah memengaruhi semuanya.
Tentu saja, Anda mungkin menghadapi situasi di mana Anda tidak akan menambahkan apa pun ke tempat sampah dalam waktu dekat - misalnya, jika Anda akan berlibur. Maka, ada baiknya membuang sampah sebelum keluar.
Ini MUNGKIN satu kali yang memaksa GC dapat membantu - jika program Anda menganggur, memori yang digunakan tidak dikumpulkan sampah karena tidak ada alokasi.
sumber
Praktik terbaiknya adalah tidak memaksa pengumpulan sampah dalam banyak kasus. (Setiap sistem yang saya kerjakan yang memaksa pengumpulan sampah, memiliki masalah yang menggarisbawahi yang jika diselesaikan akan menghilangkan kebutuhan untuk memaksa pengumpulan sampah, dan mempercepat sistem dengan sangat cepat.)
Ada beberapa kasus saat Anda mengetahui lebih banyak tentang penggunaan memori daripada pengumpul sampah. Hal ini tidak mungkin terjadi dalam aplikasi multi-pengguna, atau layanan yang merespons lebih dari satu permintaan pada satu waktu.
Namun dalam beberapa pemrosesan tipe batch Anda tahu lebih dari GC. Misalnya pertimbangkan aplikasi itu.
Anda mungkin dapat membuat kasus (setelah hati-hati) pengujian yang Anda harus memaksa pengumpulan sampah penuh setelah Anda memproses setiap file.
Kasus lain adalah layanan yang bangun setiap beberapa menit untuk memproses beberapa item, dan tidak menyimpan status apa pun saat tidur . Maka memaksakan koleksi lengkap sebelum tidur mungkin bermanfaat.
Saya lebih suka memiliki API pengumpulan sampah ketika saya bisa memberikan petunjuk tentang hal semacam ini tanpa harus memaksakan diri saya sendiri.
Lihat juga "Informasi Pertunjukan Rico Mariani "
sumber
Menurut saya contoh yang diberikan oleh Rico Mariani bagus: mungkin tepat untuk memicu GC jika ada perubahan signifikan pada status aplikasi. Misalnya, dalam editor dokumen, mungkin OK untuk memicu GC saat dokumen ditutup.
sumber
Ada beberapa pedoman umum dalam pemrograman yang bersifat mutlak. Separuh waktu, ketika seseorang mengatakan 'Anda melakukan kesalahan', mereka hanya melontarkan sejumlah dogma tertentu. Di C, dulu takut akan hal-hal seperti kode atau utas yang dapat mengubah diri sendiri, dalam bahasa GC itu memaksa GC atau sebaliknya mencegah GC berjalan.
Seperti halnya sebagian besar pedoman dan aturan praktis yang baik (dan praktik desain yang baik), ada kesempatan langka di mana masuk akal untuk bekerja di sekitar norma yang ditetapkan. Anda harus sangat yakin bahwa Anda memahami kasusnya, bahwa kasus Anda benar-benar memerlukan pembatalan praktik umum, dan bahwa Anda memahami risiko dan efek samping yang dapat Anda timbulkan. Tapi ada kasus seperti itu.
Masalah pemrograman sangat bervariasi dan membutuhkan pendekatan yang fleksibel. Saya telah melihat kasus di mana masuk akal untuk memblokir GC dalam bahasa pengumpulan sampah dan tempat-tempat yang masuk akal untuk memicunya daripada menunggu itu terjadi secara alami. 95% dari waktu, salah satu dari ini akan menjadi tanda tidak mendekati masalah dengan benar. Tapi 1 kali dalam 20, mungkin ada kasus valid yang harus dibuat untuk itu.
sumber
Saya telah belajar untuk tidak mencoba mengakali pengumpulan sampah. Dengan itu, saya hanya tetap menggunakan
using
kata kunci saat berhadapan dengan sumber daya yang tidak terkelola seperti file I / O atau koneksi database.sumber
Tidak yakin apakah ini adalah praktik terbaik, tetapi ketika bekerja dengan sejumlah besar gambar dalam satu loop (yaitu membuat dan membuang banyak objek Grafik / Gambar / Bitmap), saya secara teratur membiarkan GC.Collect.
Saya rasa saya membaca di suatu tempat bahwa GC hanya berjalan ketika program (sebagian besar) menganggur, dan bukan di tengah loop intensif, sehingga bisa terlihat seperti area di mana GC manual bisa masuk akal.
sumber
Satu kasus yang baru-baru ini saya temui yang memerlukan panggilan manual
GC.Collect()
adalah ketika bekerja dengan objek C ++ besar yang dibungkus dalam objek C ++ yang dikelola kecil, yang pada gilirannya diakses dari C #.Pengumpul sampah tidak pernah dipanggil karena jumlah memori terkelola yang digunakan dapat diabaikan, tetapi jumlah memori tidak terkelola yang digunakan sangat besar. Memanggil objek secara manual
Dispose()
akan mengharuskan saya melacak kapan objek tidak lagi dibutuhkan sendiri, sedangkan panggilanGC.Collect()
akan membersihkan objek yang tidak lagi dirujuk .....sumber
GC.AddMemoryPressure (ApproximateSizeOfUnmanagedResource)
konstruktor dan kemudianGC.RemoveMemoryPressure(addedSize)
ke finalizer. Dengan cara ini, pengumpul sampah akan berjalan secara otomatis, dengan mempertimbangkan ukuran struktur tidak terkelola yang dapat dikumpulkan. stackoverflow.com/questions/1149181/…Saya pikir Anda sudah membuat daftar praktik terbaik dan BUKAN untuk menggunakannya kecuali SANGAT diperlukan. Saya sangat menyarankan untuk melihat kode Anda secara lebih detail, menggunakan alat pembuatan profil jika diperlukan untuk menjawab pertanyaan ini terlebih dahulu.
sumber
Misalkan program Anda tidak mengalami kebocoran memori, objek terakumulasi dan tidak dapat di-GC di Gen 0 karena: 1) Mereka direferensikan untuk waktu yang lama jadi masuk ke Gen1 & Gen2; 2) Mereka adalah objek besar (> 80K) jadi masuklah ke LOH (Large Object Heap). Dan LOH tidak melakukan pemadatan seperti pada Gen0, Gen1 & Gen2.
Periksa penghitung kinerja ".NET Memory" dapatkah Anda melihat bahwa 1) masalah sebenarnya bukan masalah. Umumnya, setiap 10 Gen0 GC akan memicu 1 GC Gen1, dan setiap 10 Gen1 GC akan memicu 1 GC Gen2. Secara teoritis, GC1 & GC2 tidak akan pernah bisa di-GC jika tidak ada tekanan pada GC0 (jika penggunaan memori program benar-benar terhubung). Itu tidak pernah terjadi pada saya.
Untuk masalah 2), Anda dapat memeriksa penghitung kinerja ".NET Memory" untuk memverifikasi apakah LOH semakin membengkak. Jika ini benar-benar masalah Anda, mungkin Anda dapat membuat kumpulan objek besar seperti yang disarankan blog ini http://blogs.msdn.com/yunjin/archive/2004/01/27/63642.aspx .
sumber
Objek besar dialokasikan di LOH (tumpukan objek besar), bukan di gen 0. Jika Anda mengatakan bahwa mereka tidak dikumpulkan sampah dengan gen 0, Anda benar. Saya percaya mereka dikumpulkan hanya ketika siklus GC penuh (generasi 0, 1 dan 2) terjadi.
Karena itu, saya percaya di sisi lain GC akan menyesuaikan dan mengumpulkan memori lebih agresif ketika Anda bekerja dengan objek besar dan tekanan memori meningkat.
Sulit untuk mengatakan apakah akan mengumpulkan atau tidak dan dalam keadaan apa. Saya biasa melakukan GC.Collect () setelah membuang jendela / formulir dialog dengan banyak kontrol, dll. (Karena pada saat formulir dan kontrolnya berakhir di gen 2 karena membuat banyak contoh objek bisnis / memuat banyak data - tidak objek besar jelas), tetapi sebenarnya tidak melihat efek positif atau negatif dalam jangka panjang dengan melakukannya.
sumber
Saya ingin menambahkan bahwa: Memanggil GC.Collect () (+ WaitForPendingFinalizers ()) adalah salah satu bagian dari cerita. Seperti yang disebutkan dengan benar oleh orang lain, GC.COllect () adalah kumpulan non-deterministik dan diserahkan kepada kebijaksanaan GC itu sendiri (CLR). Meskipun Anda menambahkan panggilan ke WaitForPendingFinalizers, itu mungkin tidak bersifat deterministik. Ambil kode dari tautan msdn ini dan jalankan kode dengan iterasi loop objek sebagai 1 atau 2. Anda akan menemukan apa arti non-deterministik (menetapkan titik putus di destruktor objek). Tepatnya, destruktor tidak dipanggil ketika hanya ada 1 (atau 2) objek yang tersisa oleh Wait .. (). [Citation reqd.]
Jika kode Anda berurusan dengan sumber daya yang tidak terkelola (mis .: pegangan file eksternal), Anda harus menerapkan destruktor (atau finalizer).
Berikut ini contoh yang menarik:
Catatan : Jika Anda sudah mencoba contoh di atas dari MSDN, kode berikut akan membersihkan udara.
Saya sarankan, pertama analisis apa outputnya dan kemudian jalankan dan kemudian baca alasannya di bawah ini:
{Destruktor hanya dipanggil secara implisit setelah program berakhir. } Untuk membersihkan objek secara deterministik, seseorang harus mengimplementasikan IDisposable dan membuat panggilan eksplisit ke Dispose (). Itulah intinya! :)
sumber
Satu hal lagi, memicu GC Collect secara eksplisit mungkin TIDAK meningkatkan kinerja program Anda. Sangat mungkin memperburuk keadaan.
.NET GC dirancang dan disetel dengan baik untuk menjadi adaptif, yang berarti dapat menyesuaikan ambang GC0 / 1/2 sesuai dengan "kebiasaan" penggunaan memori program Anda. Jadi, itu akan disesuaikan dengan program Anda setelah beberapa waktu berjalan. Setelah Anda menjalankan GC.Collect secara eksplisit, ambang batas akan disetel ulang! Dan .NET harus menghabiskan waktu untuk beradaptasi dengan "kebiasaan" program Anda lagi.
Saran saya selalu percaya .NET GC. Setiap masalah memori muncul, periksa penghitung kinerja ".NET Memory" dan diagnosis kode saya sendiri.
sumber
Saran: jangan menerapkan ini atau apapun jika tidak yakin. Evaluasi kembali saat fakta diketahui, lalu lakukan sebelum / sesudah uji performa untuk memverifikasi.
sumber
IMHO, ini mirip dengan mengatakan "Jika Anda dapat membuktikan bahwa program Anda tidak akan pernah memiliki bug di masa mendatang, lanjutkanlah ..."
Secara serius, memaksa GC berguna untuk tujuan debugging / pengujian. Jika Anda merasa perlu melakukannya di lain waktu, maka Anda salah, atau program Anda dibuat salah. Bagaimanapun, solusinya tidak memaksa GC ...
sumber