DataSet ve DataTable'ı Atmalı mıyım?


197

DataSet ve DataTable her ikisi de IDisposable uygular, bu nedenle, geleneksel en iyi uygulamalarla, onların Dispose () yöntemlerini çağırmalıyım.

Ancak, şimdiye kadar okuduğum kadarıyla, DataSet ve DataTable'ın aslında yönetilmeyen kaynakları yok, bu yüzden Dispose () aslında fazla bir şey yapmıyor.

Ayrıca, using(DataSet myDataSet...)DataSet'in DataTable koleksiyonu olduğu için kullanamıyorum .

Bu nedenle, güvenli olmak için, myDataSet.Table'ları yinelemem, DataTable'ların her birini atmam ve DataSet'i atmam gerekir.

Peki, tüm DataSets ve DataTable'larımda Dispose () öğesini çağırmanın zahmetine değer mi?

Zeyilname:

DataSet'in imha edilmesi gerektiğini düşünenler için: Genel olarak, imha etme deseni kullanmaktır usingveya try..finallyDispose () işlevinin çağrılmasını garanti etmek istediğiniz için.

Ancak, bu bir koleksiyon için çirkin gerçek hızlı olur. Örneğin, Dispose () çağrılarından biri bir istisna atarsa ​​ne yaparsınız? Bir sonraki elemanı atmaya devam edebilmeniz için onu yutuyor musunuz ("kötü")?

Veya, ben sadece myDataSet.Dispose () çağırmak ve myDataSet.Tables DataTableları atmayı unutmak öneririz?


9
İmha herhangi bir istisna atmaz. Eğer öyleyse - iyi yazılmış değilse, o zaman… dene {some.Dispose (); } yakalamak {} yeterli olmalıdır. - blogs.msdn.com/b/clyon/archive/2004/09/23/233464.aspx
LukeSw

3
Çok sayıda DataSet nesnesi kullanan uygulamalarımdan birinde belirgin bir bellek sızıntısı olduğunu fark ettim. Bu nesneler için .Dispose () veya "using" bloklarını kullanmıyordum. Bu yüzden, koddan geçtim ve bir DataSet veya DataTable oluşturduğum her yere bir "kullanma" bloğu ekledim ve bellek şimdi serbest bırakıldı. Bana .Dispose () yönteminin DataSet ve DataTable için gerekli olduğuna dair sağlam bir gösterge gibi görünüyor.
dizzy.stackoverflow

Yanıtlar:


147

Bir Veri Kümesi için neden Dispose'in gerekli olmadığını açıklayan birkaç tartışma.

Atmak mı, Atmamak mı? :

DataSet'teki Dispose yöntemi SADECE kalıtımın yan etkisi nedeniyle vardır - başka bir deyişle, sonlandırmada gerçekten yararlı bir şey yapmaz.

Dispose DataTable ve DataSet nesnelerinde çağrılmalı mıdır? MVP'den bazı açıklamalar içerir:

System.data ad alanı (ADONET) yönetilmeyen kaynaklar içermiyor. Bu nedenle, kendinize özel bir şey eklemediğiniz sürece bunlardan herhangi birini atmanıza gerek yoktur.

Dispose yöntemini ve veri kümelerini anlama? Scott Allen yetkilisinden bir yorumu var:

Pratikte, çok az fayda sağladığı için nadiren bir DataSet Kullanıyoruz "

Yani, fikir birliği şu anda bir DataSet üzerinde Dispose çağırmak için iyi bir neden yoktur.


7
Sağlanan bağlantılar DataTable'ın bir tür Finalize edilebilir nesne olduğu noktasını tamamen kaçırdı. Lütfen aşağıdaki Nariman'ın cevabına bakınız.
Herman

İlginç cevap ama ne SqlConnection, SqlCommand ve SqlDataAdapter, Dispose açıkça çağrılmalıdır?
Willy

@ Birçok kişi IDisposables için bir kullanım ifadesi kullanmak düşünüyorum. using (SqlConnection cn = yeni SqlConnection (connectionString)) {kullanarak (SqlCommand cm = yeni SqlCommand (commandString, cn)) {cn.Open (); cm.ExecuteNonQuery (); }}
DOK

1
@ Evet, yönetilmeyen kaynaklar kullandıkları için kesinlikle bertaraf edilmelidir. Bir usingblok kullanarak açıkça veya örtük olarak çağrılması size bağlıdır.
D Stanley

129

Güncelleme (1 Aralık 2009):

Bu cevabı değiştirmek ve orijinal cevabın kusurlu olduğunu kabul etmek istiyorum.

Orijinal analiz , kesinleştirme gerektiren nesneler için geçerlidir ve doğru, derinlemesine bir anlayış olmadan uygulamaların yüzeyde kabul edilmemesi gerektiği nokta hala geçerlidir.

Ancak, DataSets, DataViews, DataTables'ın yapıcılarındaki sonlandırmayı bastırdığı ortaya çıkıyor - bu yüzden onlara Dispose () çağrılması açıkça hiçbir şey yapmıyor.

Muhtemelen bu, yönetilmeyen kaynaklara sahip olmadıkları için olur; bu nedenle MarshalByValueComponent'in yönetilmeyen kaynaklara izin vermesine rağmen , bu özel uygulamalara ihtiyaç yoktur ve bu nedenle sonlandırmayı bırakabilir.

(.NET yazarlarının normalde en fazla belleği işgal eden türlerde sonlandırmayı bastırmaya özen göstermeleri, bu uygulamanın genel olarak sonlandırılabilir türler için önemini ifade eder.)

Buna rağmen, .NET Framework'ün (neredeyse 8 yıl önce) başlaması oldukça şaşırtıcı olduğundan (parçaları birleştirmek için çelişkili, belirsiz bir malzemeyi elemek için esas olarak kendi cihazlarınıza bırakıldığınız için) bu ayrıntıların hala belgelenmediği zaman zaman sinir bozucu olmakla birlikte, her gün güvendiğimiz çerçevenin daha kapsamlı bir şekilde anlaşılmasını sağlar).

Çok fazla okumadan sonra, benim anlayışım:

Bir nesne tamamlanmasına gerektiriyorsa, o olabilir o ihtiyacı daha uzun bellek işgal - İşte sebebi: tanımlar bir yoketme) finalizable kabul edilir bir türünden bir yıkıcı tanımlar (veya devralır a) Her tür; b) Tahsis edildiğinde (kurucu çalışmadan önce), Sonlandırma kuyruğuna bir işaretçi yerleştirilir; c) Sonlandırılabilir bir nesne normalde 2 koleksiyonun geri kazanılmasını gerektirir (standart 1 yerine); d) Sonlandırmanın bastırılması, sonlandırma kuyruğundan bir nesneyi kaldırmaz (SOS'ta! FinalizeQueue tarafından bildirildiği gibi) Bu komut yanıltıcıdır; Sonlandırma kuyruğunda (kendi içinde) hangi nesnelerin olduğunu bilmek yardımcı olmaz; Sonlandırma kuyruğunda hangi nesnelerin bulunduğunu ve hala sonlandırma gerektirdiğini bilmek yardımcı olacaktır (bunun için bir komut var mı?)

Sonlandırmanın bastırılması, nesnenin başlığında, çalışma zamanına, Sonlandırıcının çağrılması gerekmediğini (Kırılabilir kuyruğu taşımasına gerek olmadığını) gösteren biraz kapanır; Sonuçlandırma kuyruğunda kalır (ve SOS'taki FinalizeQueue tarafından rapor edilmeye devam eder)

DataTable, DataSet, DataView sınıflarının tümü, yönetilemeyen kaynakları (potansiyel olarak) işleyebilen sonlandırılabilir bir nesne olan MarshalByValueComponent'e dayanır.

  • DataTable, DataSet, DataView yönetilmeyen kaynaklar sunmadığından, kurucularında sonlandırmayı bastırırlar
  • Bu alışılmadık bir desen olsa da, arayanı kullanımdan sonra imha etme konusunda endişelenmek zorunda kalmaz
  • Bu ve DataTable'ların potansiyel olarak farklı DataSets arasında paylaşılabilmesi gerçeği, DataSets'in alt DataTable'ları atmayı umursamamasıdır.
  • Bu aynı zamanda bu nesnelerin SOS'taki! FinalizeQueue altında görüneceği anlamına gelir
  • Bununla birlikte, bu nesneler, tamamlanamayan benzerleri gibi, tek bir koleksiyondan sonra yine de geri kazanılabilir olmalıdır

4 (yeni referanslar):

Orijinal Yanıt:

Bu konuda birçok yanıltıcı ve genellikle çok kötü cevaplar var - buraya inen herkes gürültüyü görmezden gelmeli ve aşağıdaki referansları dikkatlice okumalıdır.

Hiç şüphesiz, imha edilmelidir herhangi finalizable nesneler çağırdı.

DataTable vardır finalizable.

Dispose çağrılması belleğin geri kazanımını önemli ölçüde hızlandırır.

MarshalByValueComponent , Dispose ( ) içinde GC.SuppressFinalize (this) öğesini çağırır - bu atlama, bellek geri kazanılmadan önce yüzlerce Gen0 koleksiyonu değilse düzinelerce beklemek zorunda kalır:

Bu temel sonuçlandırma anlayışıyla, çok önemli bazı şeyleri zaten çıkarabiliriz:

İlk olarak, sonlandırılması gereken nesneler yapmayan nesnelerden daha uzun yaşar. Aslında, çok daha uzun yaşayabilirler. Örneğin, gen2'deki bir nesnenin sonlandırılması gerektiğini varsayalım. Sonlandırma zamanlanacaktır ancak nesne hala gen2'de olduğundan, bir sonraki gen2 toplama gerçekleşinceye kadar yeniden toplanmayacaktır. Bu gerçekten çok uzun bir zaman olabilir ve aslında işler yolunda giderse uzun zaman alacaktır, çünkü gen2 koleksiyonları maliyetlidir ve bu nedenle bunların çok seyrek olmasını istiyoruz. Sonlandırılması gereken daha eski nesneler, alanları geri kazanılmadan önce yüzlerce gen0 koleksiyonu olmasa bile düzinelerce beklemek zorunda kalabilir.

İkincisi, sonlandırılması gereken nesneler teminat hasarına neden olur. Dahili nesne işaretçileri geçerli kalması gerektiğinden, yalnızca doğrudan sonlandırma gerektiren nesneler bellekte kalmayacak, aynı zamanda doğrudan ve dolaylı olarak nesnenin referans verdiği her şey de bellekte kalacaktır. Eğer sonlandırma gerektiren tek bir nesne tarafından devasa bir nesne ağacı tutturulmuş olsaydı, o zaman tartıştığımız gibi potansiyel olarak uzun süre tüm ağaç oyalanacaktı. Bu nedenle sonlandırıcıları az miktarda kullanmak ve mümkün olduğunca az dahili nesne işaretçisi olan nesnelere yerleştirmek önemlidir. Az önce verdiğim ağaç örneğinde, sonlandırma ihtiyacı olan kaynakları ayrı bir nesneye taşıyarak ve ağacın kökündeki o nesneye referans tutarak sorunu kolayca önleyebilirsiniz.

Son olarak, sonlandırma gerektiren nesneler sonlandırıcı iş parçacığı için iş oluşturur. Sonlandırma işleminiz karmaşık bir işlemse, tek ve sonlandırıcı iş parçacığı bu adımları gerçekleştirmek için çok zaman harcayacaktır, bu da bir iş birikmesine neden olabilir ve bu nedenle daha fazla nesnenin sonlandırmayı beklemeye devam etmesine neden olur. Bu nedenle, sonuçlandırıcıların mümkün olduğunca az iş yapması hayati önem taşımaktadır. Ayrıca, tüm nesne işaretçileri sonlandırma sırasında geçerli kalmasına rağmen, bu işaretçilerin zaten sonlandırılmış olan nesnelere yol açmış olabileceğini ve bu nedenle yararlı olmadıklarını unutmayın. İşaretçiler geçerli olsa bile, sonlandırma kodunda nesne işaretçileri izlemekten kaçınmak genellikle en güvenlidir. Güvenli, kısa bir sonlandırma kodu yolu en iyisidir.

Gen2'de 100'lerce MB referans edilmemiş DataTables gören birinden alın: Bu çok önemlidir ve bu konudaki cevaplar tarafından tamamen kaçırılmıştır.

Referanslar:

1 - http://msdn.microsoft.com/tr-tr/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 performanslı-finalizedispose-pattern.aspx kullanılarak

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


İyi bir nokta. Birçok DataTable içeren bir DataSet'iniz olduğunda kodunuzu genellikle nasıl yapılandırırsınız? Tonlar iç içe ifadeler kullanılarak mı? Tek bir deneme ... son olarak hepsini bir kerede temizlemek için?
mbeckish

14
"Bununla birlikte, DataSets, DataViews, DataTables'ın yapıcılarındaki sonlandırmayı bastırdığı ortaya çıkıyor - bu yüzden üzerlerinde Dipose () çağırmak açıkça hiçbir şey yapmıyor." bir sekansör değildir: iki kavram büyük ölçüde ilişkisizdir; sonlandırmayı baskılayan bir şey hala Dispose () içinde bir şeyler yapabilir. Aslında, tersine çevirirsek daha mantıklıdır: Dispose () hiçbir şey yapmaz, bu yüzden yapıcıdaki sonlandırmayı bastırır, yani yapacak bir şey olmadığı için GC'yi finalizörü çağırmakla rahatsız etmek istemez ( (genellikle imha çağrısı olarak adlandırılır).
Marc Gravell

Teşekkürler. Bu tartışma TableAdapters için de geçerli mi?
Bondolin


24

Yararlı bir şey yaptığını varsaymalı ve mevcut hiçbir şey yapmasa bile Dispose olarak adlandırmalısınız. NET Framework enkarnasyonları, verimsiz kaynak kullanımına yol açan gelecekteki sürümlerde bu şekilde kalacağının garantisi yoktur.


Gelecekte de IDisposable uygulanacağının garantisi yoktur. (...) kullanmak kadar basit olsaydı sizinle aynı fikirdeydim, ama DataSet durumunda, hiçbir şey için çok fazla güçlük gibi görünüyor.
mbeckish

28
Her zaman IDisposable uygulanacağını varsaymak oldukça güvenlidir. Arabirim eklemek veya kaldırmak son derece önemli bir değişiklikken, Dispose uygulamasının değiştirilmesi değildir.
Greg Dean

5
Ayrıca, farklı bir sağlayıcı aslında IDisposable ile bir şeyler yapan bir uygulamaya sahip olabilir.
Matt Spradley

Bu DataTablemühürlü değil bahsetmiyorum - yaptığınız zaman büyük bir anlaşma değil , bir argüman olarak veya bir yöntem çağrısının sonucu olarak new DataTableoldukça önemlidir DataTable.
Luaan

17

Nesnenin yönetilmeyen kaynakları olmasa bile, imha, nesne grafiklerini kırarak GC'ye yardımcı olabilir. Genel olarak, nesne IDisposable uygularsa Dispose () çağrılmalıdır.

Dispose () öğesinin gerçekte bir şey yapıp yapmayacağı verilen sınıfa bağlıdır. DataSet durumunda, Dispose () uygulaması MarshalByValueComponent öğesinden devralınır. Kendini konteynerden kaldırır ve Disposed olayını çağırır. Kaynak kodu aşağıdadır (.NET Reflector ile sökülmüştür):

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);
                }
            }
        }
    }
}

1
Aslında. Son zamanlarda çok sayıda DataTable'ların çok büyük bir döngüde Atılmadan oluşturulduğu bazı kodlar gördüm. Bu, bilgisayarda kullanılan tüm belleğin ve bellek bittiğinde işlemin çökmesine neden olur. Geliştiriciye DataTable üzerinde dispose çağırmasını söyledikten sonra sorun ortadan kalktı.
RichardOD

7

DataTable'ları kendiniz mi oluşturuyorsunuz? Çünkü herhangi bir Nesnenin alt öğeleri üzerinden yineleme yapmak (DataSet.Table'larda olduğu gibi) genellikle gerekli değildir, çünkü tüm alt üyeleri atmak Ebeveynin görevidir.

Genel olarak kural şudur: Oluşturduysanız ve IDisposable uygularsa, atın. Eğer yaratmadıysanız, ATMAYIN, üst nesnenin işi budur. Ancak her nesnenin özel kuralları olabilir, Belgeleri kontrol edin.

Net 3.5 için, açıkça "Artık kullanmadığınızda imha et" yazıyor, ben de öyle yapardım.


4
Anladığım kadarıyla, genel fikir birliği bir nesnenin kendi yönetilmeyen kaynaklarını elden çıkarması gerektiğidir. Bununla birlikte, IDisposable nesneleri koleksiyonu, her birini atmak için genellikle öğelerini yinelemez, çünkü koleksiyonun dışındaki öğelerine başka referanslar olabilir: stackoverflow.com/questions/496722/…
mbeckish

1
Doğru, Koleksiyonlar her zaman özel olarak gördüğüm bir şeydir, çünkü genellikle hiçbir şey "yapmazlar", sadece ... Kaplar, bu yüzden bu konuda hiç rahatsız etmedim.
Michael Stum

7

Bir nesne IDisposeable uyguladığı zaman dispose diyorum. Orada bir sebep var.

Veri Kümeleri büyük bellek domuzları olabilir. Temizlik için ne kadar erken işaretlenirse o kadar iyidir.

Güncelleme

Bu soruyu cevaplamamın üzerinden 5 yıl geçti. Cevabımı hala kabul ediyorum. Bir atma yöntemi varsa, nesne ile işiniz bittiğinde çağrılmalıdır. IDispose arabirimi bir nedenle uygulanmıştır.


5
Dispose çağrısı belleğin geri kazanımını hızlandırmaz, bunun için genellikle kötü bir plan olan çöp toplayıcıyı manuel olarak başlatmanız gerekir.
Tetraneutron

2
Dispose, bir grup başvuruyu null değerine ayarlarsa, nesnelerin aksi takdirde atlanabilecek toplama adayları olmasına neden olabilir.
Greg Dean

1
Atma işleminin amacı, yönetilen nesnelerin belleğini temizlemek değildir - çöp toplayıcının işi budur. Mesele, yönetilmeyen nesneleri temizlemektir. DataSet'lerin yönetilmeyen referansları olmadığına dair kanıt var gibi görünüyor, bu yüzden teorik olarak bunları çağırmak zorunda kalmanıza gerek yok. Bununla birlikte, asla Dispose deme yolumdan çıkmak zorunda kaldığım bir durumda bulunmadım - sadece yine de ararım.
cbp

4
Birincil IDisposal kullanımı yönetilmeyen kaynakları yayınlayacak. Çoğu zaman durumu atılan bir örnek için anlamlı olacak şekilde değiştirir. (örneğin, false değerine ayarlanmış özellikler, referanslar null değerine ayarlanmış vb.)
Greg Dean

3
Bir nesnenin üzerinde bir atma yöntemi varsa, yönetilmeyen nesneleri temizleyip temizlememesine bakılmaksızın oraya bir sebeple konulmuştur.
Chuck Conway

4

Niyetiniz veya bu sorunun içeriği gerçekten çöp toplama ise, veri kümelerini ve veri tablolarını açıkça null olarak ayarlayabilir veya anahtar kelimeyi kullanarak kullanabilir ve kapsam dışına çıkabilirsiniz. Tetraneutron'un daha önce söylediği gibi imha çok işe yaramaz. GC, artık referans verilmeyen veri kümesi nesnelerini ve kapsam dışı olanları da toplar.

Gerçekten SO'yu oy kullanma zorunluluğunu, cevabı indirmeden önce bir yorum yazmaya zorlamak istiyorum.


+ 1 Sanırım bazı insanlar başkalarının farklı bakış açılarını düşünmelerine izin vermek istemiyorlar.
DOK

2
oylamada hiçbir şekilde insanların farklı bakış açılarını göz önünde bulundurmasına engel olmaz.
Greg Dean

1

Veri kümeleri, IDisposable'ı uygulayan IDisposable eksiksiz MarshalByValueComponent uygular. Veri kümeleri yönetildiğinden, imha etme çağrısının gerçek bir yararı yoktur.


6
Şimdi, daha sonra ne yapacağını bilen olabilir.
Greg Dean

Gelecekte herhangi bir kodun yapması gerekeni yapamayacağını tahmin ettiğiniz bu tutum, ilgili herkes için varsayımda bir acıdır.
MicroservicesOnDDD

0

Clear () işlevini kullanmayı deneyin. Bertaraf etmek benim için harika çalışıyor.

DataTable dt = GetDataSchema();
//populate dt, do whatever...
dt.Clear();

0

DataSet, MarshalByValueComponent sınıfını ve MarshalByValueComponent IDisposable Arabirimini uyguladığı için Dispose () yöntemine gerek yoktur

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.