Haruskah Saya Buang () DataSet dan DataTable?

197

DataSet dan DataTable keduanya mengimplementasikan IDisposable, jadi, dengan praktik terbaik konvensional, saya harus memanggil metode Buang () mereka.

Namun, dari apa yang saya baca sejauh ini, DataSet dan DataTable sebenarnya tidak memiliki sumber daya yang tidak dikelola, jadi Buang () sebenarnya tidak banyak berbuat.

Plus, saya tidak bisa hanya menggunakan using(DataSet myDataSet...)karena DataSet memiliki koleksi DataTables.

Jadi, agar aman, saya harus beralih melalui myDataSet.Table, buang masing-masing DataTable, lalu buang DataSet.

Jadi, apakah perlu untuk memanggil Buang () pada semua DataSets dan DataTables saya?

Tambahan:

Bagi Anda yang berpikir bahwa DataSet harus dibuang: Secara umum, pola untuk membuang adalah menggunakan usingatau try..finally, karena Anda ingin menjamin bahwa Buang () akan dipanggil.

Namun, ini menjadi sangat cepat untuk koleksi. Misalnya, apa yang Anda lakukan jika salah satu panggilan untuk Buang () melemparkan pengecualian? Apakah Anda menelannya (yang "buruk") sehingga Anda dapat melanjutkan untuk membuang elemen berikutnya?

Atau, apakah Anda menyarankan saya panggil myDataSet.Dispose (), dan lupakan tentang membuang DataTables di myDataSet.Tables?

mbeckish
sumber
9
Buang tidak seharusnya membuang pengecualian. Jika ya — itu tidak ditulis dengan baik, jadi ... coba {some.Dispose (); } catch {} harus cukup. - blogs.msdn.com/b/clyon/archive/2004/09/23/233464.aspx
LukeSw
3
Saya melihat ada kebocoran memori di salah satu aplikasi saya yang menggunakan banyak objek DataSet. Saya belum pernah memanggil .Dispose () atau menggunakan blok "using" untuk objek-objek itu. Jadi, saya membaca kode dan menambahkan blok "menggunakan" ke setiap tempat saya membuat DataSet atau DataTable, dan voila memori sekarang dirilis. Menurut saya indikasi kuat bahwa .Dose () sebenarnya diperlukan untuk DataSet dan DataTable.
dizzy.stackoverflow

Jawaban:

147

Berikut adalah beberapa diskusi yang menjelaskan mengapa Buang tidak diperlukan untuk DataSet.

Membuang atau Tidak Membuang? :

Metode Buang di DataSet HANYA ada karena efek samping dari pewarisan - dengan kata lain, itu tidak benar-benar melakukan sesuatu yang berguna dalam finalisasi.

Haruskah Buang dipanggil pada objek DataTable dan DataSet? termasuk beberapa penjelasan dari MVP:

System.data namespace (ADONET) tidak mengandung sumber daya yang tidak dikelola. Karena itu, Anda tidak perlu membuangnya selama Anda belum menambahkan sesuatu yang istimewa pada diri Anda.

Memahami metode Buang dan kumpulan data? memiliki dengan komentar dari otoritas Scott Allen:

Dalam praktiknya, kami jarang membuang DataSet karena menawarkan sedikit manfaat "

Jadi, konsensus ada bahwa saat ini tidak ada alasan yang baik untuk memanggil Buang di DataSet.

DOK
sumber
7
Tautan yang disediakan benar-benar melewatkan titik bahwa DataTable adalah jenis objek yang dapat diselesaikan. Silakan lihat jawaban Nariman di bawah ini.
Herman
Jawaban yang menarik tetapi bagaimana dengan SqlConnection, SqlCommand dan SqlDataAdapter, haruskah Buang disebut secara eksplisit?
Willy
@ Yah saya pikir banyak orang menggunakan pernyataan menggunakan IDisposables. using (SqlConnection cn = new SqlConnection (connectionString)) {using (SqlCommand cm = SqlCommand baru (commandString, cn)) {cn.Open (); cm.ExecuteNonQuery (); }}
DOK
1
@ Yah ya itu harus dibuang karena mereka menggunakan sumber daya yang tidak dikelola. Apakah itu disebut secara eksplisit atau implisit menggunakan usingblok terserah Anda.
D Stanley
129

Pembaruan (1 Desember 2009):

Saya ingin mengubah jawaban ini dan mengakui bahwa jawaban yang asli salah.

Analisis asli memang berlaku untuk objek yang membutuhkan finalisasi - dan titik bahwa praktik tidak boleh diterima di permukaan tanpa pemahaman yang akurat dan mendalam masih berlaku.

Namun, ternyata DataSets, DataViews, DataTables menekan finalisasi dalam konstruktor mereka - inilah mengapa memanggil Buang () pada mereka secara eksplisit tidak melakukan apa-apa.

Agaknya, ini terjadi karena mereka tidak memiliki sumber daya yang tidak dikelola; jadi terlepas dari kenyataan bahwa MarshalByValueComponent membuat penyisihan untuk sumber daya yang tidak dikelola, implementasi khusus ini tidak memiliki kebutuhan dan karenanya dapat melupakan finalisasi.

(Bahwa. NET penulis akan berhati-hati untuk menekan finalisasi pada tipe yang biasanya menempati sebagian besar memori berbicara tentang pentingnya praktik ini secara umum untuk tipe yang dapat diselesaikan.)

Sekalipun demikian, detail-detail ini masih kurang didokumentasikan sejak dimulainya .NET Framework (hampir 8 tahun yang lalu) cukup mengejutkan (bahwa Anda pada dasarnya dibiarkan menggunakan perangkat Anda sendiri untuk menyaring material yang bertentangan namun ambigu untuk menyatukan potongan-potongan tersebut. kadang membuat frustasi tetapi memberikan pemahaman yang lebih lengkap tentang kerangka kerja yang kita andalkan sehari-hari).

Setelah banyak membaca, inilah pemahaman saya:

Jika suatu objek membutuhkan finalisasi, ia dapat menempati memori lebih lama dari yang seharusnya - inilah alasannya: a) Setiap jenis yang mendefinisikan destruktor (atau mewarisi dari tipe yang mendefinisikan destruktor) dianggap dapat diselesaikan; b) Pada alokasi (sebelum konstruktor berjalan), sebuah pointer ditempatkan pada antrian Finalisasi; c) Objek yang dapat diselesaikan biasanya membutuhkan 2 koleksi untuk direklamasi (bukan standar 1); d) Menekan finalisasi tidak menghapus objek dari antrian finalisasi (seperti yang dilaporkan oleh! FinalizeQueue di SOS) Perintah ini menyesatkan; Mengetahui objek apa yang ada pada antrian finalisasi (dengan sendirinya) tidak membantu; Mengetahui objek apa yang ada pada antrian finalisasi dan masih membutuhkan finalisasi akan sangat membantu (apakah ada perintah untuk ini?)

Menekan finalisasi sedikit mematikan di header objek yang mengindikasikan ke runtime bahwa ia tidak perlu meminta Finalizer-nya dipanggil (tidak perlu memindahkan antrian FReachable); Masih dalam antrian Finalisasi (dan terus dilaporkan oleh! FinalizeQueue di SOS)

Kelas DataTable, DataSet, DataView semuanya di-root di MarshalByValueComponent, objek yang dapat diselesaikan yang dapat (berpotensi) menangani sumber daya yang tidak dikelola

  • Karena DataTable, DataSet, DataView tidak memperkenalkan sumber daya yang tidak dikelola, mereka menekan finalisasi dalam konstruktor mereka
  • Meskipun ini adalah pola yang tidak biasa, itu membebaskan penelepon dari harus khawatir tentang panggilan Buang setelah digunakan
  • Ini, dan fakta bahwa DataTable berpotensi dibagikan di berbagai DataSets, kemungkinan mengapa DataSets tidak peduli untuk membuang DataTables anak
  • Ini juga berarti bahwa objek-objek ini akan muncul di bawah! FinalizeQueue di SOS
  • Namun, objek-objek ini harus tetap dapat direklamasi setelah satu koleksi, seperti rekanannya yang tidak dapat diselesaikan

4 (referensi baru):

Jawaban asli:

Ada banyak jawaban yang menyesatkan dan umumnya sangat buruk tentang ini - siapa pun yang mendarat di sini harus mengabaikan kebisingan dan membaca referensi di bawah ini dengan cermat.

Tanpa ragu, Buang harus dipanggil pada objek yang dapat diselesaikan .

DataTables yang Finalizable.

Memanggil Buang secara signifikan mempercepat pengambilan kembali memori.

Panggilan MarshalByValueComponent GC.SuppressFinalize (this) dalam Buangnya ( ) - melewatkan ini berarti harus menunggu puluhan jika tidak ratusan koleksi Gen0 sebelum memori direklamasi:

Dengan pemahaman dasar finalisasi ini, kita dapat menyimpulkan beberapa hal yang sangat penting:

Pertama, objek yang membutuhkan finalisasi hidup lebih lama dari objek yang tidak. Bahkan, mereka bisa hidup lebih lama. Sebagai contoh, misalkan objek yang ada dalam gen2 perlu diselesaikan. Finalisasi akan dijadwalkan tetapi objek masih dalam gen2, sehingga tidak akan dikumpulkan kembali sampai pengumpulan gen2 berikutnya terjadi. Itu bisa menjadi waktu yang sangat lama, dan, pada kenyataannya, jika semuanya berjalan baik itu akan menjadi waktu yang lama, karena koleksi gen2 mahal dan dengan demikian kami ingin itu terjadi sangat jarang. Objek yang lebih tua yang membutuhkan finalisasi mungkin harus menunggu puluhan jika tidak ratusan koleksi gen0 sebelum ruang mereka direklamasi.

Kedua, objek yang membutuhkan finalisasi menyebabkan kerusakan jaminan. Karena pointer objek internal harus tetap valid, tidak hanya objek yang secara langsung membutuhkan finalisasi berlama-lama di memori, tetapi segala sesuatu yang dirujuk oleh objek, secara langsung dan tidak langsung, juga akan tetap ada dalam memori. Jika pohon besar benda berlabuh oleh satu objek yang membutuhkan finalisasi, maka seluruh pohon akan berlama-lama, berpotensi untuk waktu yang lama seperti yang baru saja kita bahas. Oleh karena itu penting untuk menggunakan finalizer dengan hemat dan menempatkannya pada objek yang memiliki pointer objek internal sesedikit mungkin. Dalam contoh pohon yang baru saja saya berikan, Anda dapat dengan mudah menghindari masalah dengan memindahkan sumber daya yang membutuhkan penyelesaian ke objek yang terpisah dan menyimpan referensi ke objek tersebut di akar pohon.

Akhirnya, objek yang membutuhkan finalisasi membuat pekerjaan untuk thread finalizer. Jika proses finalisasi Anda adalah proses yang kompleks, utas satu-satunya dan finalizer akan menghabiskan banyak waktu melakukan langkah-langkah tersebut, yang dapat menyebabkan tumpukan pekerjaan dan karena itu menyebabkan lebih banyak objek berlama-lama menunggu untuk finalisasi. Karena itu, sangat penting bagi para finalis untuk melakukan sesedikit mungkin pekerjaan. Ingat juga bahwa meskipun semua pointer objek tetap valid selama finalisasi, mungkin saja pointer tersebut mengarah ke objek yang telah diselesaikan dan karenanya mungkin kurang bermanfaat. Biasanya paling aman untuk menghindari mengikuti pointer objek dalam kode finalisasi meskipun pointer valid. Jalur kode finalisasi yang aman dan singkat adalah yang terbaik.

Ambillah dari seseorang yang melihat 100 MB dari DataTables yang tidak direferensikan di Gen2: ini sangat penting dan benar-benar terlewatkan oleh jawaban di utas ini.

Referensi:

1 - http://msdn.microsoft.com/en-us/library/ms973837.aspx

2 - http://vineetgupta.spaces.live.com/blog/cns!8DE4BDC896BEE1AD!1104.entry http://www.dotnetfunda.com/articles/article524-net-best-practice-no-2-improve-garbage -collector-performance-using-finalizedispose-pattern.aspx

3 - http://codeidol.com/csharp/net-framework/Inside-the-CLR/Automatic-Memory-Management/

Nariman
sumber
Poin yang bagus. Bagaimana Anda biasanya menyusun kode ketika Anda memiliki DataSet dengan banyak DataTable? Banyak yang bersarang menggunakan pernyataan? Sekali coba .. akhirnya membersihkan semuanya sekaligus?
mbeckish
14
Pernyataan "Namun, ternyata DataSets, DataViews, DataTables menekan finalisasi dalam konstruktor mereka - inilah mengapa memanggil Dipose () pada mereka secara eksplisit tidak melakukan apa-apa." adalah non-sequitur: kedua konsep ini sebagian besar tidak berhubungan; sesuatu yang menekan finalisasi masih bisa melakukan sesuatu di Buang (). Memang, sebenarnya lebih masuk akal jika kita membalikkannya: Buang () tidak melakukan apa-apa, itulah sebabnya ia menekan finalisasi dalam konstruktor, yaitu karena tidak ada yang perlu dilakukan, tidak ingin mengganggu GC dengan memanggil finalis ( yang biasanya panggilan buang).
Marc Gravell
Terima kasih. Apakah diskusi ini juga berlaku untuk TableAdapter?
Bondolin
24

Anda harus menganggap itu melakukan sesuatu yang berguna dan memanggil Buang meskipun itu tidak melakukan apa-apa saat ini. Inkarnasi Framework NET, tidak ada jaminan itu akan tetap seperti itu di versi masa depan yang mengarah pada penggunaan sumber daya yang tidak efisien.

Nuno
sumber
Tidak ada jaminan bahwa itu akan menerapkan IDisposable di masa depan, baik. Saya setuju dengan Anda jika sesederhana menggunakan (...), tetapi dalam kasus DataSet, sepertinya banyak kerumitan untuk apa-apa.
mbeckish
28
Cukup aman untuk menganggap bahwa itu akan selalu menerapkan IDisposable. Menambah atau menghapus antarmuka adalah perubahan besar, sedangkan mengubah implementasi Buang tidak.
Greg Dean
5
Juga, penyedia yang berbeda mungkin memiliki implementasi yang benar-benar melakukan sesuatu dengan IDisposable.
Matt Spradley
Belum lagi yang DataTabletidak disegel - bukan masalah besar ketika Anda melakukannya new DataTable, tetapi cukup penting ketika mengambil DataTablesebagai argumen atau sebagai hasil dari pemanggilan metode.
Luaan
17

Bahkan jika objek tidak memiliki sumber daya yang tidak dikelola, membuang mungkin membantu GC dengan memecah grafik objek. Secara umum, jika objek mengimplementasikan IDisposable maka Buang () harus dipanggil.

Apakah Buang () sebenarnya melakukan sesuatu atau tidak tergantung pada kelas yang diberikan. Dalam hal DataSet, Buang implementasi () diwarisi dari MarshalByValueComponent. Ini menghapus dirinya dari wadah dan memanggil acara yang dibuang. Kode sumber di bawah ini (dibongkar dengan .NET Reflector):

protected virtual void Dispose(bool disposing)
{
    if (disposing)
    {
        lock (this)
        {
            if ((this.site != null) && (this.site.Container != null))
            {
                this.site.Container.Remove(this);
            }
            if (this.events != null)
            {
                EventHandler handler = (EventHandler) this.events[EventDisposed];
                if (handler != null)
                {
                    handler(this, EventArgs.Empty);
                }
            }
        }
    }
}
dwieczor
sumber
1
Memang. Saya melihat beberapa kode baru-baru ini di mana banyak DataTable dibuat dalam loop yang sangat besar tanpa dibuang. Ini menyebabkan semua memori dikonsumsi di komputer dan proses macet karena kehabisan memori. Setelah saya mengatakan kepada pengembang untuk menelepon membuang DataTable masalahnya hilang.
RichardOD
7

Apakah Anda membuat DataTable sendiri? Karena iterasi melalui anak-anak dari Obyek apa pun (seperti dalam DataSet. Tabel) biasanya tidak diperlukan, karena itu tugas Orangtua untuk membuang semua anggota anak itu.

Secara umum, aturannya adalah: Jika Anda membuatnya dan mengimplementasikan IDisposable, Buang. Jika Anda TIDAK membuatnya, maka JANGAN membuangnya, itu pekerjaan dari objek induk. Tetapi setiap objek mungkin memiliki aturan khusus, periksa Dokumentasi.

Untuk .net 3.5, secara eksplisit tertulis "Buang ketika tidak menggunakan lagi", jadi itulah yang akan saya lakukan.

Michael Stum
sumber
4
Dari apa yang saya mengerti, konsensus umum adalah bahwa suatu objek harus membuang sumber dayanya sendiri yang tidak dikelola. Namun, kumpulan objek IDisposable pada umumnya tidak akan beralih melalui elemen-elemennya untuk membuang masing-masing, karena mungkin ada referensi lain untuk elemen-elemennya di luar koleksi: stackoverflow.com/questions/496722/…
mbeckish
1
Benar, Koleksi selalu sesuatu yang saya anggap istimewa karena mereka biasanya tidak "melakukan" apa pun, mereka hanya ... Wadah, jadi saya tidak pernah peduli tentang itu.
Michael Stum
7

Saya sebut buang kapan saja objek mengimplementasikan IDisposeable. Itu ada di sana karena suatu alasan.

DataSets bisa menjadi babi memori yang sangat besar. Semakin cepat mereka ditandai untuk dibersihkan, semakin baik.

memperbarui

Sudah 5 tahun sejak saya menjawab pertanyaan ini. Saya masih setuju dengan jawaban saya. Jika ada metode buang, itu harus dipanggil ketika Anda selesai dengan objek. Antarmuka IDispose diimplementasikan karena suatu alasan.

Chuck Conway
sumber
5
Memanggil membuang tidak mempercepat pengambilan kembali memori, untuk melakukan itu Anda harus memulai secara manual pengumpul sampah yang umumnya merupakan rencana yang buruk.
Tetraneutron
2
Jika Buang set sekelompok referensi ke nol, itu dapat menyebabkan objek menjadi kandidat untuk koleksi yang mungkin dilewati.
Greg Dean
1
Tujuan Buang bukanlah untuk menghapus memori objek yang dikelola - itu adalah pekerjaan pengumpul sampah. Intinya adalah untuk membersihkan objek yang tidak dikelola. Tampaknya ada bukti bahwa DataSets tidak memiliki referensi yang tidak dikelola sehingga secara teoritis tidak perlu dibuang memanggil mereka. Yang sedang berkata, saya tidak pernah dalam situasi di mana saya harus pergi keluar dari cara saya untuk memanggil Buang - saya akan tetap menyebutnya saja.
cbp
4
Penggunaan utama IDisposable adalah untuk melepaskan sumber daya yang tidak dikelola. Sering kali itu juga memodifikasi keadaan dengan cara yang masuk akal untuk contoh yang dibuang. (Yaitu properti diatur ke false, referensi diatur ke nol, dll)
Greg Dean
3
Jika ada metode pembuangan pada objek itu diletakkan di sana karena suatu alasan terlepas apakah itu untuk membersihkan objek yang tidak dikelola atau tidak.
Chuck Conway
4

Jika maksud Anda atau konteks pertanyaan ini benar-benar pengumpulan sampah, maka Anda dapat mengatur dataset dan data menjadi null secara eksplisit atau menggunakan kata kunci menggunakan dan membiarkannya keluar dari ruang lingkup. Buang tidak berbuat banyak seperti yang dikatakan Tetraneutron sebelumnya. GC akan mengumpulkan objek dataset yang tidak lagi direferensikan dan juga yang berada di luar cakupan.

Saya benar-benar berharap SO memaksa orang agar memilih untuk benar-benar menulis komentar sebelum menurunkan jawabannya.

Srikar Doddi
sumber
+1 Saya kira beberapa orang tidak ingin orang lain mempertimbangkan sudut pandang yang berbeda.
DOK
2
down voting, sama sekali tidak melarang orang untuk mempertimbangkan berbagai sudut pandang.
Greg Dean
1

Kumpulan data mengimplementasikan IDisposable MarshalByValueComponent menyeluruh, yang mengimplementasikan IDisposable. Karena kumpulan data dikelola, tidak ada manfaat nyata dari panggilan buang.

Tetraneutron
sumber
6
Mungkin sekarang, siapa yang tahu apa yang akan dilakukannya nanti.
Greg Dean
Sikap di mana Anda berspekulasi bahwa kode apa pun di masa depan tidak akan melakukan apa yang seharusnya dilakukan adalah rasa sakit dalam asumsi untuk semua yang terlibat.
MicroservicesOnDDD
0

Coba gunakan fungsi Hapus (). Ini bekerja bagus untuk saya saat membuang.

DataTable dt = GetDataSchema();
//populate dt, do whatever...
dt.Clear();
Hasan Savran
sumber
0

Tidak perlu Buang () karena DataSet mewarisi kelas MarshalByValueComponent dan MarshalByValueComponent mengimplementasikan IDisposable Interface

Sunil Dhappadhule
sumber