Meskipun saya benar-benar memahami implikasi serius dari bermain-main dengan fungsi ini (atau setidaknya itulah yang saya pikirkan), saya gagal untuk memahami mengapa itu menjadi salah satu hal yang tidak akan pernah digunakan oleh programmer terhormat, bahkan mereka yang bahkan tidak tahu. untuk apa ini.
Katakanlah saya sedang mengembangkan aplikasi di mana penggunaan memori sangat bervariasi tergantung pada apa yang dilakukan pengguna. Siklus hidup aplikasi dapat dibagi menjadi dua tahap utama: pengeditan dan pemrosesan waktu nyata. Selama tahap pengeditan, anggaplah miliaran atau bahkan triliunan objek tercipta; beberapa dari mereka kecil dan beberapa tidak, beberapa mungkin memiliki finalizer dan beberapa mungkin tidak, dan anggap masa hidup mereka bervariasi dari beberapa milidetik hingga berjam-jam. Selanjutnya, pengguna memutuskan untuk beralih ke tahap waktu nyata. Pada titik ini, anggaplah bahwa kinerja memainkan peran fundamental dan perubahan sekecil apa pun dalam aliran program dapat membawa konsekuensi bencana. Pembuatan objek kemudian dikurangi seminimal mungkin dengan menggunakan kumpulan objek dan semacamnya tetapi kemudian, GC berbunyi secara tak terduga dan membuang semuanya, dan seseorang mati.
Pertanyaannya: Dalam hal ini, bukankah bijaksana untuk memanggil GC.Collect () sebelum memasuki tahap kedua?
Bagaimanapun, kedua tahap ini tidak pernah tumpang tindih dalam waktu satu sama lain dan semua pengoptimalan dan statistik yang bisa dikumpulkan oleh GC akan sedikit berguna di sini ...
Catatan: Seperti yang telah Anda tunjukkan, .NET mungkin bukan platform terbaik untuk aplikasi seperti ini, tetapi itu di luar cakupan pertanyaan ini. Tujuannya adalah untuk memperjelas apakah panggilan GC.Collect () bisa meningkatkan perilaku / kinerja aplikasi secara keseluruhan atau tidak. Kita semua setuju bahwa keadaan di mana Anda akan melakukan hal seperti itu sangat jarang, tetapi sekali lagi, GC mencoba untuk menebak dan melakukannya dengan sangat baik hampir sepanjang waktu, tetapi ini masih tentang menebak.
Terima kasih.
Jawaban:
Dari Blog Rico ...
Jadi sepertinya situasi ini mungkin termasuk dalam Aturan # 2, Anda tahu bahwa ada saat di mana banyak benda tua telah mati, dan itu tidak berulang. Namun, jangan lupakan kata-kata perpisahan Rico.
Ukur, ukur, ukur.
sumber
Jika Anda memanggil GC.Collect () dalam kode produksi, pada dasarnya Anda menyatakan bahwa Anda mengetahui lebih banyak daripada penulis GC. Mungkin itu masalahnya. Namun biasanya tidak, dan oleh karena itu sangat tidak disarankan.
sumber
malloc
hanya memiliki antarmuka yang sangat sederhana, dan implementasinya dapat cukup bervariasi. Itu semua tergantung pada jenis masalah yang Anda coba selesaikan. Jikamalloc
"cukup baik", Anda tidak perlu buffer pooling, bukan? Pengembangan C / C ++ penuh dengan contoh di mana Anda mencoba menebak-nebak OS / runtime / libraries karena Anda lebih tahu (dan terkadang, Anda benar-benar tahu). Banyak aplikasi yang mengutamakan kinerja menghindari penggunaan pengalokasi sistem / waktu proses sepenuhnya. Game yang digunakan untuk mengalokasikan semua memori saat startup (array berukuran konstan, dll.).Jadi bagaimana jika Anda menggunakan objek COM seperti MS Word atau MS Excel dari .NET? Tanpa memanggil
GC.Collect
setelah melepaskan objek COM kami telah menemukan bahwa contoh aplikasi Word atau Excel masih ada.Sebenarnya kode yang kami gunakan adalah:
Jadi, apakah itu penggunaan pengumpul sampah yang salah? Jika demikian, bagaimana kita membuat objek Interop mati? Juga jika tidak dimaksudkan untuk digunakan seperti ini, mengapa adalah
GC
'sCollect
metode bahkanPublic
?sumber
Nah, GC adalah salah satu hal yang memiliki hubungan cinta / benci dengan saya. Kami telah memecahkannya di masa lalu melalui VistaDB dan membuat blog tentangnya. Mereka telah memperbaikinya, tetapi butuh waktu lama untuk mendapatkan perbaikan dari mereka untuk hal-hal seperti ini.
GC itu rumit, dan pendekatan satu ukuran untuk semua sangat, sangat sulit untuk dilakukan pada sesuatu yang sebesar ini. MS telah melakukan tugasnya dengan cukup baik, tetapi terkadang ada kemungkinan untuk menipu GC.
Secara umum, Anda tidak boleh menambahkan a
Collect
kecuali Anda tahu pasti bahwa Anda baru saja membuang banyak memori dan itu akan menuju krisis paruh baya jika GC tidak membersihkannya sekarang.Anda dapat mengacaukan seluruh mesin dengan serangkaian
GC.Collect
pernyataan buruk . Kebutuhan akan pernyataan kumpulkan hampir selalu menunjuk pada kesalahan mendasar yang lebih besar. Kebocoran memori biasanya berkaitan dengan referensi dan kurangnya pemahaman tentang cara kerjanya. Atau penggunaan fileIDisposable
on object yang tidak membutuhkannya dan meletakkan beban yang jauh lebih tinggi pada GC.Perhatikan dengan cermat% waktu yang dihabiskan di GC melalui penghitung kinerja sistem. Jika Anda melihat aplikasi Anda menggunakan 20% atau lebih waktunya di GC, Anda memiliki masalah pengelolaan objek yang serius (atau pola penggunaan yang tidak normal). Anda ingin selalu meminimalkan waktu yang dihabiskan GC karena ini akan mempercepat seluruh aplikasi Anda.
Penting juga untuk dicatat bahwa GC di server berbeda dengan workstation. Saya telah melihat sejumlah masalah kecil yang sulit dilacak dengan orang-orang yang tidak menguji keduanya (atau bahkan tidak menyadari bahwa mereka adalah keduanya).
Dan agar jawaban saya selengkap mungkin, Anda juga harus menguji di bawah Mono jika Anda menargetkan platform itu juga. Karena ini adalah implementasi yang sangat berbeda, itu mungkin mengalami masalah yang sama sekali berbeda dari implementasi MS.
sumber
Ada situasi di mana itu berguna, tetapi secara umum itu harus dihindari. Anda dapat membandingkannya dengan GOTO, atau mengendarai moped: Anda melakukannya saat diperlukan, tetapi Anda tidak memberi tahu teman Anda tentang hal itu.
sumber
Dari pengalaman saya, tidak pernah disarankan untuk melakukan panggilan ke GC.Collect () dalam kode produksi. Dalam debugging, ya, ada keuntungannya untuk membantu mengklarifikasi potensi kebocoran memori. Saya kira alasan mendasar saya adalah bahwa GC telah ditulis dan dioptimalkan oleh programmer jauh lebih pintar daripada saya, dan jika saya sampai pada suatu titik di mana saya merasa perlu memanggil GC.Collect () itu adalah petunjuk bahwa saya telah keluar jalur di suatu tempat. Dalam situasi Anda, sepertinya Anda tidak benar-benar memiliki masalah memori, hanya saja Anda khawatir tentang ketidakstabilan apa yang akan dibawa oleh koleksi ke proses Anda. Melihat bahwa itu tidak akan membersihkan objek yang masih digunakan, dan menyesuaikan dengan sangat cepat baik untuk naik maupun turunnya permintaan, saya rasa Anda tidak perlu mengkhawatirkannya.
sumber
Salah satu alasan terbesar untuk memanggil GC.Collect () adalah saat Anda baru saja melakukan peristiwa penting yang menghasilkan banyak sampah, seperti yang Anda gambarkan. Memanggil GC.Collect () bisa menjadi ide bagus di sini; jika tidak, GC mungkin tidak memahami bahwa ini adalah peristiwa 'satu kali'.
Tentu saja, Anda harus membuat profil, dan melihat sendiri.
sumber
Nah, jelas Anda tidak boleh menulis kode dengan persyaratan waktu nyata dalam bahasa dengan pengumpulan sampah non-waktu nyata.
Dalam kasus dengan tahapan yang ditentukan dengan baik, tidak ada masalah dengan memicu pengumpul sampah. Tapi kasus ini sangat jarang terjadi. Masalahnya adalah bahwa banyak pengembang akan mencoba menggunakan ini untuk masalah kertas-over dengan gaya kultus kargo, dan menambahkannya tanpa pandang bulu akan menyebabkan masalah kinerja.
sumber
Memanggil GC.Collect () memaksa CLR melakukan stack walk untuk melihat apakah setiap objek benar-benar bisa dilepaskan dengan memeriksa referensi. Ini akan memengaruhi skalabilitas jika jumlah objeknya banyak, dan juga diketahui memicu pengumpulan sampah terlalu sering. Percayai CLR dan biarkan pengumpul sampah berjalan sendiri saat diperlukan.
sumber
Faktanya, menurut saya bukan praktik yang sangat buruk untuk menelepon GC.Collect.
Mungkin ada kasus ketika kita membutuhkannya. Sebagai contoh, saya memiliki formulir yang menjalankan utas, yang kemudian membuka tabel berbeda dalam database, mengekstrak konten di bidang BLOB ke file temp, mengenkripsi file, lalu membaca file tersebut ke dalam binarystream dan kembali ke BLOB bidang di tabel lain.
Seluruh operasi membutuhkan banyak memori, dan tidak pasti tentang jumlah baris dan ukuran konten file dalam tabel.
Saya dulu sering mendapatkan OutofMemory Exception dan saya pikir akan bijaksana untuk menjalankan GC.Collect secara berkala berdasarkan variabel counter. Saya menambah penghitung dan ketika level tertentu tercapai, GC dipanggil untuk mengumpulkan sampah apa pun yang mungkin telah terbentuk, dan untuk mendapatkan kembali memori yang hilang karena kebocoran memori yang tidak terduga.
Setelah ini, saya pikir ini bekerja dengan baik, setidaknya tidak terkecuali !!!
Saya menelepon dengan cara berikut:
sumber
Di bawah .net, waktu yang dibutuhkan untuk melakukan pengumpulan sampah jauh lebih terkait dengan jumlah barang yang bukan sampah, daripada jumlah barang yang ada. Memang, kecuali sebuah objek menimpa
Finalize
(baik secara eksplisit, atau melalui destruktor C #), adalah target dari aWeakReference
, duduk di Tumpukan Objek Besar, atau khusus dalam beberapa cara terkait gc lainnya, satu-satunya hal yang mengidentifikasi memori di mana ia berada sebagai objek adalah adanya referensi yang mengakar padanya. Jika tidak, operasi GC dianalogikan dengan mengambil dari sebuah bangunan segala sesuatu yang bernilai, dan mendinamisasi bangunan tersebut, membangun yang baru di lokasi yang lama, dan meletakkan semua barang berharga di dalamnya. Upaya yang diperlukan untuk mendinam bangunan benar-benar tidak tergantung pada jumlah sampah di dalamnya.Akibatnya, menelepon
GC.Collect
cenderung meningkatkan jumlah keseluruhan pekerjaan yang harus dilakukan sistem. Ini akan menunda terjadinya pengumpulan berikutnya, tetapi mungkin akan melakukan pekerjaan yang sama secepat yang diperlukan pengumpulan berikutnya ketika itu terjadi; pada titik ketika koleksi berikutnya akan terjadi, jumlah total waktu yang dihabiskan mengumpulkan akan telah hampir sama seperti yangGC.Collect
tidak dipanggil, tetapi sistem akan telah mengumpulkan beberapa sampah, menyebabkan koleksi berhasil akan diperlukan lebih cepat dari punyaGC.Collect
tidak telah dipanggil.Saat-saat yang saya lihat
GC.Collect
benar-benar berguna adalah ketika seseorang perlu mengukur penggunaan memori dari beberapa kode (karena angka penggunaan memori hanya benar-benar berarti setelah koleksi), atau profil mana dari beberapa algoritme yang lebih baik (memanggil GC.Collect () sebelum menjalankan masing-masing dari beberapa bagian kode dapat membantu memastikan status dasar yang konsisten). Ada beberapa kasus lain di mana seseorang mungkin mengetahui hal-hal yang tidak diketahui GC, tetapi kecuali seseorang menulis program single-threaded, tidak mungkin orang mengetahui bahwaGC.Collect
panggilan yang akan membantu struktur data satu thread menghindari "krisis paruh baya. "tidak akan menyebabkan data thread lain mengalami" krisis paruh baya "yang seharusnya dapat dihindari.sumber
Membuat gambar dalam satu lingkaran - bahkan jika Anda memanggil buang, memori tidak dipulihkan. Kumpulkan sampah setiap saat. Saya beralih dari memori 1,7GB pada aplikasi pemrosesan foto saya menjadi 24MB dan kinerjanya luar biasa.
Benar-benar ada waktu yang Anda perlukan untuk menghubungi GC.Collect.
sumber
Dispose
yang tidak seharusnya melepaskan memori dikelola. Anda sepertinya tidak tahu bagaimana model memori di .NET bekerja.Kami memiliki masalah serupa dengan pengumpul sampah yang tidak mengumpulkan sampah dan mengosongkan memori.
Dalam program kami, kami memproses beberapa Excel Spreadsheets berukuran sedang dengan OpenXML. Spreadsheets berisi 5 sampai 10 "lembar" dengan sekitar 1000 baris 14 kolom.
Program dalam lingkungan 32 bit (x86) akan macet dengan kesalahan "kehabisan memori". Kami berhasil menjalankannya di lingkungan x64, tetapi kami menginginkan solusi yang lebih baik.
Kami menemukan satu.
Berikut adalah beberapa fragmen kode yang disederhanakan dari apa yang tidak berfungsi dan apa yang berhasil ketika harus secara eksplisit memanggil Pengumpul Sampah untuk membebaskan memori dari objek yang dibuang.
Memanggil GC dari dalam subrutin tidak berhasil. Memori tidak pernah diambil kembali ...
Dengan Memindahkan panggilan GC ke luar lingkup subrutin, sampah dikumpulkan dan memori dibebaskan.
Saya harap ini membantu orang lain yang frustrasi dengan pengumpulan sampah .NET ketika tampaknya mengabaikan panggilan ke
GC.Collect()
.Paul Smith
sumber
Tidak ada yang salah dengan meminta koleksi secara eksplisit. Beberapa orang hanya ingin percaya bahwa jika itu adalah layanan yang disediakan oleh vendor, jangan mempersoalkannya. Oh, dan semua itu membeku secara acak pada saat yang salah dalam aplikasi interaktif Anda? Versi selanjutnya akan membuatnya lebih baik!
Membiarkan proses latar belakang berurusan dengan manipulasi memori berarti tidak harus menghadapinya sendiri, benar. Tetapi secara logis ini tidak berarti bahwa yang terbaik bagi kita adalah tidak menanganinya sendiri dalam segala keadaan. GC dioptimalkan untuk banyak kasus. Tetapi ini tidak secara logis berarti bahwa ini dioptimalkan di semua kasus.
Pernahkah Anda menjawab pertanyaan terbuka seperti 'mana yang merupakan algoritma pengurutan terbaik' dengan jawaban yang pasti? Jika demikian, jangan sentuh GC. Bagi Anda yang menanyakan kondisi, atau memberikan jawaban tipe 'dalam kasus ini', Anda dapat melanjutkan untuk mempelajari tentang GC dan kapan harus mengaktifkannya.
Harus dikatakan, aplikasi saya macet di Chrome dan Firefox yang membuat saya frustasi, dan bahkan untuk beberapa kasus memori tumbuh tanpa hambatan - Seandainya saja mereka belajar memanggil pengumpul sampah - atau memberi saya sehingga saat saya mulai membaca teks halaman, saya dapat menekannya dan bebas dari macet selama 20 menit berikutnya.
sumber
Saya pikir Anda benar tentang skenario, tetapi saya tidak yakin tentang API.
Microsoft mengatakan bahwa dalam kasus seperti itu Anda harus menambahkan tekanan memori sebagai petunjuk kepada GC bahwa GC harus segera melakukan pengumpulan.
sumber
Apakah ada yang salah? Fakta bahwa Anda menebak-nebak pengumpul sampah dan pengalokasi memori, yang di antara keduanya memiliki gagasan yang jauh lebih besar tentang penggunaan memori aktual aplikasi Anda pada waktu proses daripada yang Anda lakukan.
sumber
Keinginan untuk memanggil GC.Collect () biasanya mencoba menutupi kesalahan yang Anda buat di tempat lain!
Akan lebih baik jika Anda menemukan di mana Anda lupa membuang barang yang tidak Anda butuhkan lagi.
sumber
Intinya, Anda dapat membuat profil aplikasi dan melihat bagaimana koleksi tambahan ini memengaruhi banyak hal. Saya menyarankan untuk menjauh darinya kecuali jika Anda akan membuat profil. GC dirancang untuk menjaga dirinya sendiri dan seiring dengan perkembangan runtime, mereka dapat meningkatkan efisiensi. Anda tidak ingin banyak kode berkeliaran yang dapat mengacaukan pekerjaan dan tidak dapat memanfaatkan peningkatan ini. Ada argumen serupa untuk menggunakan foreach alih-alih for, yaitu, bahwa perbaikan masa depan di bawah sampul dapat ditambahkan ke foreach dan kode Anda tidak perlu diubah untuk memanfaatkannya.
sumber
.NETFramework itu sendiri tidak pernah dirancang untuk berjalan dalam lingkungan waktu nyata. Jika Anda benar-benar membutuhkan pemrosesan waktu nyata, Anda akan menggunakan bahasa waktu nyata tertanam yang tidak didasarkan pada .NET atau menggunakan .NET Compact Framework yang dijalankan pada perangkat Windows CE.
sumber
Hal terburuk yang akan dilakukannya adalah membuat program Anda berhenti sebentar. Jadi jika Anda setuju, lakukanlah. Biasanya tidak diperlukan untuk klien tebal atau aplikasi web dengan sebagian besar interaksi pengguna.
Saya telah menemukan bahwa terkadang program dengan utas yang berjalan lama, atau program batch, akan mendapatkan pengecualian OutOfMemory meskipun mereka membuang objek dengan benar. Satu yang saya ingat adalah pemrosesan transaksi database lini bisnis; yang lainnya adalah rutinitas pengindeksan pada thread latar belakang di aplikasi klien yang tebal.
Dalam kedua kasus tersebut, hasilnya sederhana: Tidak ada GC. Kumpulkan, kehabisan memori, secara konsisten; GC. Kumpulkan, kinerja tanpa cela.
Saya sudah mencobanya untuk memecahkan masalah memori beberapa kali, tetapi tidak berhasil. Saya mengeluarkannya.
Singkatnya, jangan memasukkannya kecuali Anda mendapatkan kesalahan. Jika Anda memasukkannya dan tidak memperbaiki masalah memori, keluarkan kembali. Ingatlah untuk menguji dalam mode Rilis dan bandingkan apel dengan apel.
Satu-satunya saat hal-hal bisa salah dengan ini adalah ketika Anda bersikap moralistik tentangnya. Ini bukan masalah nilai; banyak programmer telah meninggal dan langsung masuk surga dengan banyak GC.Collects yang tidak perlu dalam kode mereka, yang hidup lebih lama dari mereka.
sumber