Saya tahu dari membaca dokumentasi Microsoft bahwa "utama" penggunaan IDisposable
antarmuka adalah untuk membersihkan sumber daya yang tidak dikelola.
Bagi saya, "tidak terkelola" berarti hal-hal seperti koneksi basis data, soket, pegangan jendela, dll. Tapi, saya telah melihat kode di mana Dispose()
metode ini diterapkan untuk sumber daya yang dikelola gratis , yang tampaknya berlebihan bagi saya, karena pengumpul sampah harus mengurus itu untukmu
Sebagai contoh:
public class MyCollection : IDisposable
{
private List<String> _theList = new List<String>();
private Dictionary<String, Point> _theDict = new Dictionary<String, Point>();
// Die, clear it up! (free unmanaged resources)
public void Dispose()
{
_theList.clear();
_theDict.clear();
_theList = null;
_theDict = null;
}
Pertanyaan saya adalah, apakah ini membuat pemulung bebas memori yang digunakan MyCollection
lebih cepat dari biasanya?
sunting : Sejauh ini orang telah memposting beberapa contoh penggunaan IDisposable yang baik untuk membersihkan sumber daya yang tidak dikelola seperti koneksi basis data dan bitmap. Tapi anggaplah bahwa _theList
dalam kode di atas berisi sejuta string, dan Anda ingin membebaskan memori itu sekarang , daripada menunggu pengumpul sampah. Apakah kode di atas mencapai itu?
IDisposable
tidak menandai apa pun. TheDispose
Metode melakukan apa yang harus dilakukan untuk membersihkan sumber daya yang digunakan oleh instance. Ini tidak ada hubungannya dengan GC.IDisposable
. Dan itulah sebabnya saya mengatakan bahwa jawaban yang diterima tidak menjawab pertanyaan yang dimaksud OP (dan tindak lanjut edit) tentang apakah IDisposable akan membantu dalam <i> membebaskan memori </i>. KarenaIDisposable
tidak ada hubungannya dengan membebaskan memori, hanya sumber daya, maka seperti yang Anda katakan, tidak perlu mengatur referensi yang dikelola menjadi nol sama sekali seperti yang dilakukan OP dalam contohnya. Jadi, jawaban yang benar untuk pertanyaannya adalah "Tidak, itu tidak membantu membebaskan memori lebih cepat. Bahkan, itu sama sekali tidak membantu membebaskan memori, hanya sumber daya". Tapi bagaimanapun, terima kasih atas masukan Anda.Jawaban:
Tujuan Buang adalah membebaskan sumber daya yang tidak dikelola. Itu perlu dilakukan di beberapa titik, kalau tidak mereka tidak akan pernah dibersihkan. Pengumpul sampah tidak tahu cara memanggil
DeleteHandle()
variabel tipeIntPtr
, tidak tahu apakah perlu memanggil atau tidakDeleteHandle()
.Objek yang Anda buat perlu mengekspos beberapa metode, yang dapat dipanggil oleh dunia luar, untuk membersihkan sumber daya yang tidak dikelola. Metode ini dapat dinamai apa pun yang Anda suka:
atau
Tetapi sebaliknya ada nama standar untuk metode ini:
Bahkan ada antarmuka yang dibuat
IDisposable
,, yang hanya memiliki satu metode:Jadi Anda membuat objek Anda mengekspos
IDisposable
antarmuka, dan dengan cara itu Anda berjanji bahwa Anda telah menulis metode tunggal untuk membersihkan sumber daya yang tidak dikelola:Dan kamu sudah selesai. Kecuali Anda bisa berbuat lebih baik.
Bagaimana jika objek Anda telah mengalokasikan System.Drawing.Bitmap 250MB (mis. Kelas Bitmap yang dikelola .NET) sebagai semacam bingkai penyangga? Tentu, ini adalah objek .NET yang dikelola, dan pengumpul sampah akan membebaskannya. Tapi apakah Anda benar-benar ingin meninggalkan memori 250MB hanya duduk di sana - menunggu pengumpul sampah akhirnya datang dan membebaskannya? Bagaimana jika ada koneksi database terbuka ? Tentunya kita tidak ingin koneksi itu terbuka, menunggu GC menyelesaikan objek.
Jika pengguna telah menelepon
Dispose()
(artinya mereka tidak lagi berencana untuk menggunakan objek) mengapa tidak menyingkirkan bitmap yang boros dan koneksi database?Jadi sekarang kita akan:
Jadi, mari perbarui
Dispose()
metode kami untuk menyingkirkan objek yang dikelola tersebut:Dan semuanya baik-baik saja, kecuali Anda bisa berbuat lebih baik !
Bagaimana jika orang tersebut lupa memanggil
Dispose()
objek Anda? Kemudian mereka akan membocorkan beberapa sumber daya yang tidak dikelola !Jika orang itu lupa menelepon
Dispose()
, kita masih bisa menyimpan bacon mereka! Kami masih memiliki cara untuk menyebutnya untuk mereka: ketika pengumpul sampah akhirnya berhasil membebaskan (yaitu menyelesaikan) objek kami.Penghancuran objek kita oleh pengumpul Sampah adalah waktu yang tepat untuk membebaskan sumber daya tak terkelola yang sial itu. Kami melakukan ini dengan mengganti
Finalize()
metode.Tetapi ada bug dalam kode itu. Anda lihat, pengumpul sampah berjalan di utas latar belakang ; Anda tidak tahu urutan dua objek dihancurkan. Sangat mungkin bahwa dalam
Dispose()
kode Anda , objek terkelola yang Anda coba singkirkan (karena Anda ingin membantu) sudah tidak ada lagi:Jadi yang Anda butuhkan adalah cara untuk
Finalize()
mengatakanDispose()
bahwa itu tidak boleh menyentuh sumber daya yang dikelola (karena mereka mungkin tidak ada lagi), sementara masih membebaskan sumber daya yang tidak dikelola.Pola standar untuk melakukan ini adalah memiliki
Finalize()
danDispose()
keduanya memanggil metode ketiga (!); tempat Anda menyampaikan pepatah Boolean jika Anda memanggilnya dariDispose()
(berlawanan denganFinalize()
), artinya aman untuk sumber daya yang dikelola gratis.Metode internal ini dapat diberi beberapa nama arbitrer seperti "CoreDispose", atau "MyInternalDispose", tetapi adalah tradisi untuk menyebutnya
Dispose(Boolean)
:Tetapi nama parameter yang lebih bermanfaat mungkin:
Dan Anda mengubah penerapan
IDisposable.Dispose()
metode ini menjadi:dan finalizer Anda ke:
Dan semuanya baik-baik saja, kecuali Anda bisa berbuat lebih baik !
Jika pengguna memanggil
Dispose()
objek Anda, maka semuanya telah dibersihkan. Kemudian, ketika pemulung datang dan memanggil Selesai, ia akan memanggilDispose
lagi.Tidak hanya boros, tetapi jika objek Anda memiliki referensi sampah ke objek yang sudah Anda buang dari panggilan terakhir
Dispose()
, Anda akan mencoba membuangnya lagi!Anda akan melihat dalam kode saya, saya berhati-hati untuk menghapus referensi ke objek yang telah saya buang, jadi saya tidak mencoba untuk memanggil
Dispose
referensi objek sampah. Tapi itu tidak menghentikan bug halus masuk.Ketika pengguna memanggil
Dispose()
: pegangan CursorFileBitmapIconServiceHandle dihancurkan. Kemudian ketika pengumpul sampah berjalan, ia akan mencoba untuk menghancurkan pegangan yang sama lagi.Cara Anda memperbaiki ini adalah memberi tahu pengumpul sampah bahwa tidak perlu repot menyelesaikan objek - sumber dayanya sudah dibersihkan, dan tidak ada lagi pekerjaan yang diperlukan. Anda melakukan ini dengan memanggil
GC.SuppressFinalize()
dalamDispose()
metode:Sekarang setelah pengguna menelepon
Dispose()
, kami memiliki:Tidak ada gunanya dalam GC menjalankan finalizer - semuanya diurus.
Tidak bisakah saya menggunakan Finalisasi untuk membersihkan sumber daya yang tidak dikelola?
Dokumentasi untuk
Object.Finalize
mengatakan:Tetapi dokumentasi MSDN juga mengatakan, untuk
IDisposable.Dispose
:Jadi yang mana? Yang mana tempat bagi saya untuk membersihkan sumber daya yang tidak dikelola? Jawabannya adalah:
Anda tentu dapat menempatkan pembersihan yang tidak dikelola di finalizer:
Masalahnya adalah Anda tidak tahu kapan pemulung akan menyelesaikan objek Anda. Sumber daya asli Anda yang tidak dikelola, tidak dibutuhkan, dan tidak digunakan akan tetap ada sampai pemulung akhirnya berjalan. Maka itu akan memanggil metode finalizer Anda; membersihkan sumber daya yang tidak dikelola. Dokumentasi Object.Finalize menunjukkan ini:
Ini adalah keutamaan menggunakan
Dispose
untuk membersihkan sumber daya yang tidak dikelola; Anda mengenal, dan mengontrol, ketika sumber daya yang tidak dikelola dibersihkan. Kehancuran mereka "deterministik" .Untuk menjawab pertanyaan awal Anda: Mengapa tidak melepaskan memori sekarang, daripada ketika GC memutuskan untuk melakukannya? Saya memiliki perangkat lunak pengenal wajah yang perlu menyingkirkan 530 MB gambar internal sekarang , karena mereka tidak lagi diperlukan. Ketika kita tidak melakukannya: mesin terhenti.
Pembacaan Bonus
Bagi siapa pun yang menyukai gaya jawaban ini (menjelaskan mengapa , bagaimana caranya menjadi jelas), saya sarankan Anda membaca Bab Satu dari COM Essential Don Box:
Dalam 35 halaman dia menjelaskan masalah menggunakan objek biner, dan menemukan COM di depan mata Anda. Setelah Anda menyadari mengapa COM, 300 halaman sisanya jelas, dan hanya merinci implementasi Microsoft.
Saya pikir setiap programmer yang pernah berurusan dengan objek atau COM harus, paling tidak, membaca bab pertama. Itu adalah penjelasan terbaik dari apa pun.
Bacaan Bonus Ekstra
Ketika semua yang Anda tahu salah oleh Eric Lippert
sumber
null
. Pertama-tama, itu berarti Anda tidak dapat membuatnyareadonly
, dan kedua, Anda harus melakukan!=null
pemeriksaan yang sangat jelek (seperti pada kode contoh). Anda bisa memiliki benderadisposed
, tetapi lebih mudah untuk tidak menghiraukannya. .NET GC cukup agresif sehingga referensi ke suatu bidangx
tidak lagi dihitung 'digunakan' saat melewatix.Dispose()
garis.IDisposable
sering digunakan untuk mengeksploitasiusing
pernyataan dan mengambil keuntungan dari cara mudah untuk melakukan pembersihan deterministik objek yang dikelola.sumber
Tujuan dari pola Buang adalah untuk menyediakan mekanisme untuk membersihkan sumber daya yang dikelola dan tidak dikelola dan kapan itu terjadi tergantung pada bagaimana metode Buang disebut. Dalam contoh Anda, penggunaan Buang sebenarnya tidak melakukan sesuatu yang berhubungan dengan membuang, karena membersihkan daftar tidak berdampak pada koleksi yang dibuang. Demikian juga, panggilan untuk mengatur variabel ke nol juga tidak berdampak pada GC.
Anda dapat melihat artikel ini untuk detail lebih lanjut tentang cara menerapkan pola Buang, tetapi pada dasarnya terlihat seperti ini:
Metode yang paling penting di sini adalah Buang (bool), yang sebenarnya berjalan dalam dua keadaan berbeda:
Masalah dengan hanya membiarkan GC melakukan pembersihan adalah bahwa Anda tidak memiliki kontrol nyata atas kapan GC akan menjalankan siklus pengumpulan (Anda dapat menghubungi GC.Collect (), tetapi Anda benar-benar tidak seharusnya) sehingga sumber daya dapat tetap ada sekitar lebih lama dari yang dibutuhkan. Ingat, memanggil Buang () sebenarnya tidak menyebabkan siklus pengumpulan atau dengan cara apa pun menyebabkan GC mengumpulkan / membebaskan objek; itu hanya menyediakan sarana untuk secara lebih deterministik membersihkan sumber daya yang digunakan dan memberi tahu GC bahwa pembersihan ini telah dilakukan.
Inti dari IDisposable dan pola buang bukan tentang segera membebaskan memori. Satu-satunya waktu panggilan untuk Buang sebenarnya akan memiliki kesempatan untuk segera membebaskan memori adalah ketika menangani == skenario salah membuang dan memanipulasi sumber daya yang tidak dikelola. Untuk kode yang dikelola, memori tidak akan benar-benar direklamasi sampai GC menjalankan siklus pengumpulan, yang Anda benar-benar tidak memiliki kendali atas (selain memanggil GC.Collect (), yang telah saya sebutkan bukan ide yang baik).
Skenario Anda tidak benar-benar valid karena string. NET tidak menggunakan sumber daya yang tidak diubah dan tidak menerapkan IDisposable, tidak ada cara untuk memaksa mereka untuk "dibersihkan."
sumber
Seharusnya tidak ada panggilan lebih lanjut ke metode objek setelah Buang dipanggil itu (meskipun objek harus mentolerir panggilan lebih lanjut untuk Buang). Karena itu contoh dalam pertanyaan itu konyol. Jika Buang disebut, maka objek itu sendiri dapat dibuang. Jadi pengguna harus membuang semua referensi ke seluruh objek (atur ke nol) dan semua objek yang terkait di dalamnya akan secara otomatis dibersihkan.
Adapun pertanyaan umum tentang dikelola / tidak dikelola dan diskusi dalam jawaban lain, saya pikir jawaban untuk pertanyaan ini harus dimulai dengan definisi sumber daya yang tidak dikelola.
Intinya adalah bahwa ada fungsi yang dapat Anda panggil untuk membuat sistem dalam keadaan, dan ada fungsi lain yang dapat Anda panggil untuk mengembalikannya dari keadaan itu. Sekarang, dalam contoh umum, yang pertama mungkin merupakan fungsi yang mengembalikan penanganan file, dan yang kedua mungkin menjadi panggilan
CloseHandle
.Tapi - dan ini kuncinya - mereka bisa menjadi pasangan fungsi yang cocok. Yang satu membangun negara, yang lain meruntuhkannya. Jika negara telah dibangun tetapi belum dirobohkan, maka ada sumber daya yang ada. Anda harus mengatur agar teardown terjadi pada waktu yang tepat - sumber daya tidak dikelola oleh CLR. Satu-satunya jenis sumber daya yang dikelola secara otomatis adalah memori. Ada dua macam: GC, dan stack. Tipe nilai dikelola oleh stack (atau dengan menumpang tumpangan di dalam tipe referensi), dan tipe referensi dikelola oleh GC.
Fungsi-fungsi ini dapat menyebabkan perubahan status yang dapat disisipkan secara bebas, atau mungkin perlu disarangkan dengan sempurna. Perubahan status mungkin threadsafe, atau mungkin tidak.
Lihatlah contoh dalam pertanyaan Keadilan. Perubahan pada indentasi file Log harus bersarang dengan sempurna, atau semuanya salah. Juga mereka tidak mungkin threadsafe.
Dimungkinkan untuk mencari tumpangan dengan pengumpul sampah untuk membersihkan sumber daya yang tidak terkelola. Tetapi hanya jika fungsi perubahan negara adalah threadsafe dan dua negara dapat memiliki masa hidup yang tumpang tindih dengan cara apa pun. Jadi contoh keadilan dari sumber daya TIDAK harus memiliki finalizer! Itu tidak akan membantu siapa pun.
Untuk sumber daya semacam itu, Anda bisa menerapkan
IDisposable
, tanpa finalizer. Finalizer benar-benar opsional - harus. Ini dipoles atau bahkan tidak disebutkan dalam banyak buku.Anda kemudian harus menggunakan
using
pernyataan untuk memiliki kesempatan memastikan bahwaDispose
itu disebut. Ini pada dasarnya seperti menumpang tumpangan dengan tumpukan (jadi finalizer adalah untuk GC,using
adalah untuk tumpukan).Bagian yang hilang adalah bahwa Anda harus menulis secara manual Buang dan buat itu memanggil bidang Anda dan kelas dasar Anda. Pemrogram C ++ / CLI tidak harus melakukan itu. Compiler menulisnya untuk mereka dalam banyak kasus.
Ada alternatif, yang saya lebih suka untuk negara-negara yang bersarang dengan sempurna dan bukan threadsafe (terlepas dari hal lain, menghindari IDisposable membuat Anda kesulitan memiliki argumen dengan seseorang yang tidak bisa menolak menambahkan finalizer ke setiap kelas yang mengimplementasikan IDisposable) .
Alih-alih menulis kelas, Anda menulis fungsi. Fungsi menerima delegasi untuk menelepon kembali ke:
Dan kemudian contoh sederhana adalah:
Lambda yang disahkan berfungsi sebagai blok kode, jadi seperti Anda membuat struktur kontrol Anda sendiri untuk melayani tujuan yang sama dengan
using
, kecuali bahwa Anda tidak lagi memiliki bahaya penelepon menyalahgunakannya. Tidak mungkin mereka gagal membersihkan sumber daya.Teknik ini kurang berguna jika sumber daya adalah jenis yang mungkin memiliki masa hidup yang tumpang tindih, karena Anda ingin dapat membangun sumber daya A, lalu sumber daya B, lalu membunuh sumber daya A dan kemudian membunuh sumber daya B. Anda tidak dapat melakukan itu jika Anda telah memaksa pengguna untuk bersarang dengan sempurna seperti ini. Tapi kemudian Anda perlu menggunakan
IDisposable
(tetapi masih tanpa finalizer, kecuali Anda telah mengimplementasikan threadsafety, yang tidak gratis).sumber
enter
danexit
merupakan inti dari bagaimana saya memikirkan sumber daya. Berlangganan / berhenti berlangganan pada acara harus sesuai dengan itu tanpa kesulitan. Dalam hal karakteristik ortogonal / sepadan itu praktis tidak bisa dibedakan dari kebocoran memori. (Ini tidak mengejutkan karena berlangganan hanya menambahkan objek ke daftar.)Skenario yang saya lakukan menggunakan IDisposable: bersihkan sumber daya yang tidak dikelola, berhenti berlangganan untuk acara, tutup koneksi
Ungkapan yang saya gunakan untuk mengimplementasikan IDisposable ( bukan threadsafe ):
sumber
Yap, kode itu benar-benar berlebihan dan tidak perlu dan itu tidak membuat pengumpul sampah melakukan apa pun yang tidak akan dilakukannya (sekali contoh MyCollection keluar dari ruang lingkup, yaitu.) Terutama
.Clear()
panggilan.Jawab untuk hasil edit Anda: Semacam. Jika saya melakukan ini:
Secara fungsional identik dengan ini untuk keperluan manajemen memori:
Jika Anda benar-benar benar-benar perlu membebaskan memori ini seketika, hubungi
GC.Collect()
. Tidak ada alasan untuk melakukan ini di sini. Memori akan dibebaskan saat dibutuhkan.sumber
Jika
MyCollection
akan menjadi sampah yang dikumpulkan, maka Anda tidak perlu membuangnya. Melakukan hal itu hanya akan mengocok CPU lebih dari yang diperlukan, dan bahkan dapat membatalkan beberapa analisis pra-perhitungan bahwa pemulung telah melakukan.Saya menggunakan
IDisposable
untuk melakukan hal-hal seperti memastikan utas dibuang dengan benar, bersama dengan sumber daya yang tidak dikelola.EDIT Menanggapi komentar Scott:
Secara konseptual, GC mempertahankan pandangan dari grafik referensi objek, dan semua referensi darinya dari tumpukan frame dari thread. Tumpukan ini bisa sangat besar dan menjangkau banyak halaman memori. Sebagai optimasi, GC melakukan cache analisisnya pada halaman yang tidak mungkin berubah sangat sering untuk menghindari pemindaian ulang halaman yang tidak perlu. GC menerima pemberitahuan dari kernel ketika data dalam halaman berubah, sehingga ia tahu bahwa halaman tersebut kotor dan membutuhkan pemindaian ulang. Jika koleksi ada di Gen0 maka kemungkinan hal-hal lain di halaman juga berubah, tetapi ini kurang mungkin di Gen1 dan Gen2. Secara anekdot, kait ini tidak tersedia di Mac OS X untuk tim yang mem-porting GC ke Mac untuk mendapatkan plug-in Silverlight bekerja pada platform itu.
Poin lain terhadap pembuangan sumber daya yang tidak perlu: bayangkan situasi di mana suatu proses sedang dibongkar. Bayangkan juga bahwa proses telah berjalan selama beberapa waktu. Kemungkinannya adalah banyak halaman memori proses tersebut telah ditukar ke disk. Paling tidak mereka tidak lagi dalam cache L1 atau L2. Dalam situasi seperti itu tidak ada gunanya aplikasi yang membongkar untuk menukar semua data dan kode halaman kembali ke memori untuk 'melepaskan' sumber daya yang akan dirilis oleh sistem operasi ketika proses berakhir. Ini berlaku untuk sumber daya yang dikelola dan bahkan sumber daya tertentu yang tidak dikelola. Hanya sumber daya yang membuat utas non-latar belakang tetap hidup yang harus dibuang, jika tidak prosesnya akan tetap hidup.
Sekarang, selama eksekusi normal ada sumber daya sesaat yang harus dibersihkan dengan benar (seperti @fezmonkey menunjukkan koneksi database, soket, pegangan jendela ) untuk menghindari kebocoran memori yang tidak dikelola. Ini adalah hal-hal yang harus dibuang. Jika Anda membuat beberapa kelas yang memiliki utas (dan oleh yang saya maksud adalah ia menciptakannya dan karenanya bertanggung jawab untuk memastikannya berhenti, setidaknya dengan gaya pengkodean saya), maka kelas itu kemungkinan besar harus menerapkan
IDisposable
dan meruntuhkan utas selamaDispose
.Kerangka NET. Menggunakan
IDisposable
antarmuka sebagai sinyal, bahkan peringatan, untuk pengembang bahwa kelas ini harus dibuang. Saya tidak bisa memikirkan jenis apa pun dalam kerangka kerja yang menerapkanIDisposable
(tidak termasuk implementasi antarmuka eksplisit) di mana pembuangan adalah opsional.sumber
Dispose()
panggilan opsional , lihat: stackoverflow.com/questions/913228/…Pada contoh yang Anda posting, masih tidak "membebaskan memori sekarang". Semua memori adalah sampah yang dikumpulkan, tetapi itu memungkinkan memori dikumpulkan pada generasi sebelumnya . Anda harus menjalankan beberapa tes untuk memastikan.
Pedoman Kerangka Desain adalah pedoman, dan bukan aturan. Mereka memberi tahu Anda apa antarmuka utamanya, kapan menggunakannya, bagaimana menggunakannya, dan kapan tidak menggunakannya.
Saya pernah membaca kode yang merupakan RollBack sederhana () tentang kegagalan menggunakan IDisposable. Kelas MiniTx di bawah ini akan memeriksa bendera pada Buang () dan jika
Commit
panggilan tidak pernah terjadi maka akan memanggilRollback
dirinya sendiri. Itu menambahkan lapisan tipuan membuat kode panggilan jauh lebih mudah untuk dipahami dan dipelihara. Hasilnya terlihat seperti:Saya juga melihat timing / logging code melakukan hal yang sama. Dalam hal ini metode Buang () menghentikan timer dan mencatat bahwa blok telah keluar.
Jadi di sini adalah beberapa contoh nyata yang tidak melakukan pembersihan sumber daya yang tidak dikelola, tetapi berhasil menggunakan IDisposable untuk membuat kode bersih.
sumber
Jika Anda ingin menghapus sekarang , gunakan memori yang tidak dikelola .
Lihat:
sumber
Saya tidak akan mengulangi hal-hal yang biasa tentang Menggunakan atau membebaskan sumber daya yang tidak dikelola, yang semuanya telah dibahas. Tetapi saya ingin menunjukkan apa yang tampaknya merupakan kesalahpahaman umum.
Diberi kode berikut
Saya menyadari bahwa penerapan Disposable tidak mengikuti pedoman saat ini, tetapi mudah-mudahan Anda semua mendapatkan idenya.
Sekarang, ketika Buang dipanggil, berapa banyak memori yang dibebaskan?
Jawab: Tidak ada.
Memanggil Buang dapat melepaskan sumber daya yang tidak dikelola, itu TIDAK BISA merebut kembali memori yang dikelola, hanya GC yang bisa melakukannya. Itu tidak berarti bahwa di atas bukan ide yang baik, mengikuti pola di atas sebenarnya masih ide yang baik. Setelah Buang dijalankan, tidak ada yang menghentikan GC mengklaim kembali memori yang sedang digunakan oleh _Large, meskipun instance LargeStuff mungkin masih dalam ruang lingkup. String di _Large mungkin juga ada di gen 0 tetapi instance LargeStuff mungkin gen 2, jadi sekali lagi, memori akan diklaim kembali lebih cepat.
Tidak ada gunanya menambahkan finaliser untuk memanggil metode Buang yang ditunjukkan di atas. Itu hanya akan Tunda klaim ulang memori untuk memungkinkan finaliser berjalan.
sumber
LargeStuff
telah ada cukup lama untuk sampai ke Generasi 2, dan jika_Large
memegang referensi ke string yang baru dibuat yang ada di Generasi 0, maka jika turunanLargeStuff
ditinggalkan tanpa nulling_Large
, maka string yang dirujuk oleh_Large
akan disimpan sampai koleksi Gen2 berikutnya. Mengabaikan_Large
dapat membiarkan string dihilangkan pada koleksi Gen0 berikutnya. Dalam kebanyakan kasus, membatalkan referensi tidak membantu, tetapi ada kasus di mana ia dapat menawarkan beberapa manfaat.Terlepas dari penggunaan utamanya sebagai cara untuk mengontrol seumur hidup dari sumber daya sistem (benar-benar tertutup oleh jawaban mengagumkan dari Ian , pujian!), Yang IDisposable / menggunakan combo juga dapat digunakan untuk ruang lingkup perubahan keadaan sumber daya (kritis) global yang : yang konsol , yang benang , yang proses , setiap obyek global seperti contoh aplikasi .
Saya telah menulis artikel tentang pola ini: http://pragmateek.com/c-scope-your-global-state-changes-with-idisposable-and-the-using-statement/
Ini menggambarkan bagaimana Anda dapat melindungi beberapa negara global yang sering digunakan dengan cara yang dapat digunakan kembali dan dibaca : warna konsol , kultur utas saat ini , properti objek aplikasi Excel ...
sumber
Jika ada, saya berharap kode menjadi kurang efisien daripada ketika meninggalkannya.
Memanggil metode Clear () tidak perlu, dan GC mungkin tidak akan melakukannya jika Buang tidak melakukannya ...
sumber
Ada beberapa hal yang dilakukan
Dispose()
operasi dalam kode contoh yang mungkin memiliki efek yang tidak akan terjadi karena GC objek normalMyCollection
.Jika benda yang dirujuk oleh
_theList
atau_theDict
dirujuk oleh benda lain, maka bendaList<>
atauDictionary<>
benda itu tidak akan dikoleksi tetapi tiba-tiba tidak memiliki konten. Jika tidak ada operasi Buang () seperti dalam contoh, koleksi tersebut akan tetap berisi kontennya.Tentu saja, jika ini adalah situasi yang saya sebut itu desain yang rusak - Saya hanya menunjukkan (dengan rasa pedas, saya kira) bahwa
Dispose()
operasi mungkin tidak sepenuhnya berlebihan, tergantung pada apakah ada kegunaan lain dariList<>
atauDictionary<>
yang tidak ditunjukkan dalam fragmen.sumber
Satu masalah dengan sebagian besar diskusi tentang "sumber daya yang tidak dikelola" adalah bahwa mereka tidak benar-benar mendefinisikan istilah, tetapi tampaknya menyiratkan bahwa itu ada hubungannya dengan kode yang tidak dikelola. Meskipun benar bahwa banyak jenis sumber daya yang tidak dikelola melakukan antarmuka dengan kode yang tidak dikelola, memikirkan sumber daya yang tidak dikelola dalam istilah tersebut tidak membantu.
Alih-alih, seseorang harus mengakui kesamaan semua sumber daya yang dikelola: mereka semua mensyaratkan suatu objek yang meminta beberapa 'hal' di luar untuk melakukan sesuatu atas namanya, dengan merugikan beberapa 'hal' lain, dan entitas lain setuju untuk melakukannya sampai pemberitahuan selanjutnya. Jika objek itu akan ditinggalkan dan lenyap tanpa jejak, tidak ada yang akan mengatakan bahwa di luar 'hal' yang tidak perlu lagi mengubah perilakunya atas nama objek yang tidak lagi ada; akibatnya, kegunaan benda itu akan berkurang secara permanen.
Sumber daya yang tidak dikelola, kemudian, merepresentasikan suatu persetujuan oleh 'benda' luar untuk mengubah perilakunya atas nama suatu objek, yang akan sia-sia merusak kegunaan dari 'benda' luar itu jika objek tersebut ditinggalkan dan tidak ada lagi. Sumber daya yang dikelola adalah objek yang merupakan penerima manfaat dari perjanjian semacam itu, tetapi yang telah mendaftar untuk menerima pemberitahuan jika ditinggalkan, dan yang akan menggunakan pemberitahuan tersebut untuk membereskan urusannya sebelum dihancurkan.
sumber
IDisposable
baik untuk berhenti berlangganan dari acara.sumber
Definisi pertama. Bagi saya sumber daya yang tidak dikelola berarti beberapa kelas, yang mengimplementasikan antarmuka IDisposable atau sesuatu yang dibuat dengan penggunaan panggilan ke dll. GC tidak tahu bagaimana menangani objek seperti itu. Jika kelas hanya memiliki tipe nilai saja, maka saya tidak menganggap kelas ini sebagai kelas dengan sumber daya yang tidak dikelola. Untuk kode saya, saya mengikuti praktik selanjutnya:
following menunjukkan apa yang saya jelaskan dalam kata-kata sebagai contoh kode:
sumber
is IDisposable
seharusnya dianggap sebagai sumber daya yang tidak dikelola? Tampaknya itu tidak benar. Juga jika tipe implementasi adalah tipe nilai murni Anda tampaknya menyarankan bahwa itu tidak perlu dibuang. Itu juga tampaknya salah.Contoh kode yang Anda berikan bukan contoh yang baik untuk
IDisposable
digunakan. Kliring kamus biasanya tidak masuk keDispose
metode. Item kamus akan dihapus dan dibuang ketika keluar dari ruang lingkup.IDisposable
implementasi diperlukan untuk membebaskan beberapa memori / penangan yang tidak akan melepaskan / membebaskan bahkan setelah mereka keluar dari jangkauan.Contoh berikut menunjukkan contoh yang baik untuk pola IDisposable dengan beberapa kode dan komentar.
sumber
Kasus penggunaan yang paling dapat dibenarkan untuk pembuangan sumber daya yang dikelola, adalah persiapan bagi GC untuk merebut kembali sumber daya yang sebelumnya tidak akan pernah dikumpulkan.
Contoh utama adalah referensi melingkar.
Meskipun praktik terbaik untuk menggunakan pola yang menghindari referensi melingkar, jika Anda berakhir dengan (misalnya) objek 'anak' yang memiliki referensi kembali ke 'induknya', ini dapat menghentikan koleksi GC dari induk jika Anda hanya meninggalkan referensi dan mengandalkan GC - plus jika Anda telah mengimplementasikan finalizer, itu tidak akan pernah dipanggil.
Satu-satunya cara untuk menyelesaikan ini adalah dengan mematahkan referensi melingkar secara manual dengan mengatur referensi Orang Tua ke nol pada anak-anak.
Menerapkan IDisposable pada orang tua dan anak-anak adalah cara terbaik untuk melakukan ini. Ketika Buang dipanggil pada Orang Tua, panggil Buang semua Anak, dan dalam metode Buang anak, atur referensi Orang Tua ke nol.
sumber
WeakReference
, sistem akan memeriksa bendera yang menunjukkan bahwa referensi rooted hidup ditemukan dalam siklus GC terakhir , dan akan menambahkan objek ke antrian objek yang membutuhkan finalisasi segera, melepaskan objek dari tumpukan objek besar, atau membatalkan referensi lemah. Referensi sirkular tidak akan membuat benda hidup jika tidak ada referensi lainnya.Saya melihat banyak jawaban telah bergeser untuk berbicara tentang penggunaan IDisposable untuk sumber daya yang dikelola dan tidak dikelola. Saya menyarankan artikel ini sebagai salah satu penjelasan terbaik yang saya temukan untuk bagaimana IDisposable seharusnya digunakan.
https://www.codeproject.com/Articles/29534/IDisposable-What-Your-Mother-Never-Told-You-About
Untuk pertanyaan aktual; Jika Anda menggunakan IDisposable untuk membersihkan objek yang dikelola yang menggunakan banyak memori, jawaban singkatnya adalah tidak . Alasannya adalah bahwa sekali Anda Buang IDisposable Anda harus membiarkannya keluar dari ruang lingkup. Pada titik itu, setiap objek anak yang dirujuk juga di luar jangkauan dan akan dikumpulkan.
Satu-satunya pengecualian nyata untuk ini adalah jika Anda memiliki banyak memori yang diikat dalam objek yang dikelola dan Anda telah memblokir utas itu menunggu beberapa operasi selesai. Jika objek-objek itu di mana tidak akan diperlukan setelah panggilan itu selesai maka pengaturan referensi tersebut ke nol mungkin memungkinkan pengumpul sampah untuk mengumpulkan mereka lebih cepat. Tapi skenario itu akan merepresentasikan kode buruk yang perlu di refactored - bukan use case IDisposable.
sumber