Czy powinienem usunąć () DataSet i DataTable?


197

Zarówno DataSet, jak i DataTable implementują IDisposable, więc zgodnie z tradycyjnymi najlepszymi praktykami powinienem wywołać ich metody Dispose ().

Jednak z tego, co przeczytałem do tej pory, DataSet i DataTable nie mają w rzeczywistości żadnych niezarządzanych zasobów, więc Dispose () tak naprawdę niewiele robi.

Ponadto nie mogę po prostu użyć, using(DataSet myDataSet...)ponieważ DataSet ma kolekcję DataTables.

Tak więc, aby być bezpiecznym, musiałbym iterować przez myDataSet.Tables, pozbyć się każdego z DataTables, a następnie pozbyć się DataSet.

Czy warto zatem wywoływać funkcję Dispose () na wszystkich moich DataSets i DataTables?

Uzupełnienie:

Dla tych z Was, którzy uważają, że DataSet powinien zostać usunięty: Zasadniczo wzorzec usuwania jest używany usinglub try..finally, ponieważ chcesz zagwarantować, że zostanie wywołane Dispose ().

Jednak staje się to naprawdę brzydkie jak na kolekcję. Na przykład, co robisz, jeśli jedno z wywołań funkcji Dispose () zgłosiło wyjątek? Czy połykasz go (co jest „złe”), abyś mógł dalej pozbywać się następnego elementu?

Czy sugerujesz, żebym po prostu wywołał funkcję myDataSet.Dispose () i zapomniałeś o usuwaniu DataTables w myDataSet.Tables?


9
Utylizacja nie powinna generować żadnych wyjątków. Jeśli tak, to nie jest dobrze napisane, więc… spróbuj {some.Dispose (); } catch {} powinno wystarczyć. - blogs.msdn.com/b/clyon/archive/2004/09/23/233464.aspx
LukeSw

3
Zauważyłem widoczny wyciek pamięci w jednej z moich aplikacji, która wykorzystuje wiele obiektów DataSet. Nie wywoływałem funkcji .Dispose () ani nie korzystałem z bloków „using” dla tych obiektów. Więc przejrzałem kod i dodałem blok „za pomocą” do każdego miejsca, w którym tworzyłem DataSet lub DataTable, i voila pamięć jest teraz zwolniona. Wydaje mi się solidnym wskazaniem, że funkcja .Dispose () jest w rzeczywistości konieczna dla DataSet i DataTable.
dizzy.stackoverflow

Odpowiedzi:


147

Oto kilka dyskusji wyjaśniających, dlaczego Dispose nie jest konieczne dla DataSet.

Pozbyć się czy nie wyrzucić? :

Metoda Dispose w DataSet istnieje TYLKO z powodu skutków ubocznych dziedziczenia - innymi słowy, w rzeczywistości nie robi nic użytecznego w finalizacji.

Czy należy wywoływać Dispose na obiektach DataTable i DataSet? zawiera wyjaśnienia MVP:

Przestrzeń nazw system.data (ADONET) nie zawiera niezarządzanych zasobów. Dlatego nie musisz usuwać żadnego z nich, dopóki nie dodasz do tego czegoś specjalnego.

Zrozumienie metody Dispose i zestawów danych? ma komentarz Autorytetu Scott Allen:

W praktyce rzadko pozbywamy się zestawu danych, ponieważ oferuje on niewielkie korzyści ”

Tak więc istnieje konsensus, że obecnie nie ma dobrego powodu, aby wywoływać Dispose on DataSet.


7
Podane łącza całkowicie pomijały punkt, w którym DataTable jest rodzajem obiektu finalizowalnego. Zobacz odpowiedź Narimana poniżej.
Herman

Interesująca odpowiedź, ale co z SqlConnection, SqlCommand i SqlDataAdapter, czy Dispose należy nazwać jawnie?
Willy

@Willy Myślę, że wielu ludzi używa instrukcji ID dla IDisposables. using (SqlConnection cn = new SqlConnection (connectionString)) {using (SqlCommand cm = new SqlCommand (commandString, cn)) {cn.Open (); cm.ExecuteNonQuery (); }}
DOK

1
@ Bardzo dobrze, te powinny zostać absolutnie usunięte, ponieważ wykorzystują niezarządzane zasoby. To, czy zostanie wywołane jawnie, czy niejawnie za pomocą usingbloku, zależy od Ciebie.
D Stanley

129

Aktualizacja (1 grudnia 2009 r.):

Chciałbym zmienić tę odpowiedź i przyznać, że pierwotna odpowiedź była błędna.

Oryginalna analiza ma zastosowanie do obiektów wymagających finalizacji - a argument, że praktyki nie powinny być akceptowane na powierzchni bez dokładnego i dogłębnego zrozumienia, nadal obowiązuje.

Okazuje się jednak, że DataSets, DataViews, DataTables blokują finalizację w swoich konstruktorach - dlatego wywołanie na nich Dispose () jawnie nic nie robi.

Przypuszczalnie dzieje się tak, ponieważ nie mają niezarządzanych zasobów; więc pomimo faktu, że MarshalByValueComponent uwzględnia rezerwy na niezarządzane zasoby, te konkretne wdrożenia nie mają takiej potrzeby i dlatego mogą zrezygnować z finalizacji.

(Autorzy .NET zadbają o to, aby finalizacja tego typu typów, które zwykle zajmują najwięcej pamięci, świadczy o znaczeniu tej praktyki w ogóle dla typów finalizowanych).

Niezależnie od tego, że szczegóły te są nadal niedokumentowane, odkąd system .NET Framework (prawie 8 lat temu) jest dość zaskakujący (że zasadniczo pozostawiasz swoje własne urządzenia do przesiewania przez sprzeczne, niejednoznaczne materiały, aby połączyć elementy razem jest czasami frustrujący, ale zapewnia pełniejsze zrozumienie ram, na których codziennie polegamy).

Po wielu lekturach, oto moje zrozumienie:

Jeśli obiekt wymaga finalizacji, może zajmować pamięć dłużej niż jest to konieczne - oto dlaczego: a) Każdy typ, który definiuje destruktor (lub dziedziczy po typie, który definiuje destruktor) jest uznawany za finalizowalny; b) Przy alokacji (przed uruchomieniem konstruktora) wskaźnik umieszczany jest w kolejce finalizacji; c) Obiekt finalizowalny zwykle wymaga Wyłączenia finalizacji nieco wyłącza nagłówek obiektu, wskazując środowisku wykonawczemu, że nie musi być wywoływany jego finalizator (nie musi przenosić kolejki FReachable); Pozostaje w kolejce finalizacji (i nadal jest zgłaszany przez! FinalizeQueue w SOS) 2 kolekcji. odzyskania (zamiast standardowej 1); d) Pomijanie finalizacji nie usuwa obiektu z kolejki finalizacji (jak informuje! FinalizeQueue w SOS) To polecenie wprowadza w błąd; Wiedza, które obiekty znajdują się w kolejce finalizacji (sama w sobie), nie jest pomocna; Pomocna byłaby wiedza, które obiekty znajdują się w kolejce finalizacji i nadal wymagają finalizacji (czy istnieje na to polecenie?)

Wszystkie klasy DataTable, DataSet, DataView są zakorzenione w MarshalByValueComponent, finalizowanym obiekcie, który może (potencjalnie) obsługiwać niezarządzane zasoby

  • Ponieważ DataTable, DataSet, DataView nie wprowadzają niezarządzanych zasobów, tłumią finalizację w swoich konstruktorach
  • Chociaż jest to niezwykły wzór, uwalnia dzwoniącego od konieczności martwienia się o połączenie Usuń po użyciu
  • To oraz fakt, że DataTables mogą być potencjalnie współużytkowane przez różne DataSets, jest prawdopodobnie przyczyną, dla której DataSets nie chce pozbywać się potomnych DataTables
  • Oznacza to również, że obiekty te pojawią się pod! FinalizeQueue w SOS
  • Jednak obiekty te nadal powinny być możliwe do odzyskania po pojedynczej kolekcji, podobnie jak ich nieukończone odpowiedniki

4 (nowe referencje):

Oryginalna odpowiedź:

Istnieje wiele mylących i ogólnie bardzo słabych odpowiedzi na ten temat - każdy, kto tu wylądował, powinien zignorować hałas i uważnie przeczytać poniższe odniesienia.

Bez wątpienia należy wywoływać Dispose na dowolnych obiektach, które można sfinalizować.

Tabele danych można sfinalizować.

Wywołanie Dispose znacznie przyspiesza odzyskiwanie pamięci.

MarshalByValueComponent wywołuje GC.SuppressFinalize (this) w swoim Dispose () - pominięcie tego oznacza konieczność oczekiwania na dziesiątki, jeśli nie setki kolekcji Gen0, zanim pamięć zostanie odzyskana:

Dzięki temu podstawowemu zrozumieniu finalizacji możemy już wywnioskować kilka bardzo ważnych rzeczy:

Po pierwsze, obiekty wymagające finalizacji żyją dłużej niż obiekty, które tego nie robią. W rzeczywistości mogą żyć znacznie dłużej. Załóżmy na przykład, że obiekt należący do gen2 musi zostać sfinalizowany. Finalizacja zostanie zaplanowana, ale obiekt nadal znajduje się w gen2, więc nie zostanie ponownie zebrany, dopóki nie nastąpi kolejna kolekcja gen2. To może być naprawdę bardzo długi czas, a tak naprawdę, jeśli wszystko pójdzie dobrze, minie dużo czasu, ponieważ kolekcje gen2 są kosztowne i dlatego chcemy, aby zdarzały się bardzo rzadko. Starsze obiekty wymagające finalizacji mogą potrzebować dziesiątek, jeśli nie setek kolekcji gen0, zanim ich przestrzeń zostanie odzyskana.

Po drugie, obiekty wymagające finalizacji powodują uszkodzenia dodatkowe. Ponieważ wewnętrzne wskaźniki obiektów muszą pozostać ważne, nie tylko obiekty bezpośrednio wymagające finalizacji pozostaną w pamięci, ale wszystko, do czego odnosi się obiekt, bezpośrednio i pośrednio, pozostanie również w pamięci. Gdyby ogromne drzewo obiektów zostało zakotwiczone przez pojedynczy obiekt, który wymagał sfinalizowania, całe drzewo pozostałoby, potencjalnie przez długi czas, jak właśnie dyskutowaliśmy. Dlatego ważne jest oszczędne używanie finalizatorów i umieszczanie ich na obiektach, które mają jak najmniej wewnętrznych wskaźników obiektów. W przykładzie drzewa, który właśnie podałem, możesz łatwo uniknąć problemu, przenosząc zasoby wymagające finalizacji do osobnego obiektu i zachowując odniesienie do tego obiektu w katalogu głównym drzewa.

Wreszcie obiekty wymagające finalizacji tworzą pracę dla wątku finalizatora. Jeśli proces finalizacji jest skomplikowany, jedyny wątek finalizatora poświęci dużo czasu na wykonanie tych kroków, co może spowodować zaległości w pracy, a tym samym spowodować, że więcej obiektów pozostanie w oczekiwaniu na finalizację. Dlatego niezwykle ważne jest, aby finaliści wykonali jak najmniej pracy. Pamiętaj również, że chociaż wszystkie wskaźniki obiektów pozostają ważne podczas finalizacji, może się zdarzyć, że wskaźniki te prowadzą do obiektów, które zostały już sfinalizowane i dlatego mogą być mniej niż przydatne. Zasadniczo najbezpieczniej jest unikać śledzenia wskaźników obiektu w kodzie finalizacji, nawet jeśli wskaźniki są prawidłowe. Najlepsza jest bezpieczna, krótka ścieżka kodu finalizacji.

Weź to od kogoś, kto widział 100 MB danych DataTables bez odniesienia w Gen2: jest to niezwykle ważne i całkowicie pomijane przez odpowiedzi w tym wątku.

Bibliografia:

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/


Słuszna uwaga. Jak zwykle tworzysz swój kod, gdy masz DataSet z wieloma DataTables? Tony zagnieżdżone za pomocą instrukcji? Jedna próba… na koniec, aby wszystko to wyczyścić naraz?
mbeckish

14
Stwierdzenie „Okazuje się jednak, że DataSets, DataViews, DataTables blokują finalizację w swoich konstruktorach - dlatego wywołanie na nich Dipose () jawnie nic nie robi”. jest non-sequitur: te dwa pojęcia są w dużej mierze niezwiązane; coś, co tłumi finalizację, może nadal zrobić coś w Dispose (). W rzeczywistości ma to większy sens, jeśli go odwrócimy: Dispose () nic nie robi, dlatego tłumi finalizację w konstruktorze, tj. Ponieważ nie byłoby nic do zrobienia, nie chce przeszkadzać GC wywołaniem finalizatora ( które zazwyczaj wywołują usuwanie).
Marc Gravell

Dzięki. Czy ta dyskusja dotyczy również TableAdapters?
Bondolin


24

Powinieneś założyć, że robi coś pożytecznego i wywołać Dispose, nawet jeśli w tej chwili nic nie robi. Wcielenia NET Framework, nie ma gwarancji, że tak pozostanie w przyszłych wersjach, prowadząc do nieefektywnego wykorzystania zasobów.


Nie ma też gwarancji, że w przyszłości zaimplementuje IDisposable. Zgodziłbym się z tobą, gdyby to było tak proste, jak użycie (...), ale w przypadku DataSet wydaje się, że to wiele kłopotów za nic.
mbeckish

28
Można dość bezpiecznie założyć, że zawsze będzie implementował IDisposable. Dodanie lub usunięcie interfejsu jest przełomową zmianą, natomiast zmiana implementacji Dispose nie.
Greg Dean

5
Ponadto inny dostawca może mieć implementację, która faktycznie robi coś z IDisposable.
Matt Spradley

Nie wspominając o tym, że DataTablenie jest zapieczętowane - nie jest to wielka sprawa, gdy robisz new DataTable, ale dość ważne, gdy bierzesz DataTablejako argument lub w wyniku wywołania metody.
Luaan,

17

Nawet jeśli obiekt nie ma niezarządzanych zasobów, usunięcie może pomóc GC poprzez zerwanie wykresów obiektów. Ogólnie, jeśli obiekt implementuje IDisposable, wówczas należy wywołać metodę Dispose ().

To, czy Dispose () faktycznie coś robi, czy nie, zależy od danej klasy. W przypadku DataSet implementacja Dispose () jest dziedziczona z MarshalByValueComponent. Usuwa się z kontenera i wywołuje zdarzenie Disposed. Kod źródłowy jest poniżej (zdemontowany z .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);
                }
            }
        }
    }
}

1
W rzeczy samej. Niedawno widziałem trochę kodu, w którym wiele DataTables zostało utworzonych w bardzo dużej pętli bez konieczności usuwania. Doprowadziło to do zużycia całej pamięci na komputerze, a proces zawiesił się, gdy zabrakło pamięci. Po tym, jak powiedziałem deweloperowi, aby zadzwonił do dyspozycji na DataTable, problem zniknął.
RichardOD

7

Czy sam tworzysz DataTables? Ponieważ iteracja przez elementy potomne dowolnego obiektu (jak w DataSet.Tables) zwykle nie jest potrzebna, ponieważ zadaniem rodzica jest usunięcie wszystkich jego elementów potomnych.

Ogólnie reguła jest następująca: jeśli go utworzyłeś i implementuje on IDisposable, usuń go. Jeśli NIE utworzyłeś go, NIE usuwaj go, to zadanie obiektu nadrzędnego. Ale każdy obiekt może mieć specjalne reguły, sprawdź dokumentację.

W przypadku .net 3.5 wyraźnie mówi „Zutylizuj go, gdy już nie używasz”, więc to właśnie zrobiłbym.


4
Z tego, co rozumiem, ogólny konsensus jest taki, że obiekt powinien dysponować własnymi niezarządzanymi zasobami. Jednak zbiór obiektów IDisposable na ogół nie będzie iterował swoich elementów, aby pozbyć się każdego z nich, ponieważ mogą istnieć inne odniesienia do jego elementów poza kolekcją: stackoverflow.com/questions/496722/…
mbeckish

1
To prawda, że ​​kolekcje są zawsze czymś, co uważam za wyjątkowe, ponieważ zwykle niczego nie „robią”, są po prostu… Pojemnikami, więc nigdy się tym nie przejmowałem.
Michael Stum

7

Wywołuję dispose za każdym razem, gdy obiekt implementuje IDisposeable. Jest tam z jakiegoś powodu.

DataSets może być ogromną pamięcią pamięci. Im szybciej zostaną oznaczone do posprzątania, tym lepiej.

aktualizacja

Minęło 5 lat, odkąd odpowiedziałem na to pytanie. Nadal zgadzam się z moją odpowiedzią. Jeśli istnieje metoda usuwania, należy ją wywołać po zakończeniu pracy z obiektem. Interfejs IDispose został zaimplementowany z jakiegoś powodu.


5
Wywołanie dispose nie przyspiesza odzyskiwania pamięci, aby to zrobić, trzeba ręcznie uruchomić moduł wyrzucania elementów bezużytecznych, co jest ogólnie złym planem.
Tetraneutron

2
Jeśli Dispose ustawia wiązkę odniesień na null, może to powodować, że obiekty będą kandydatami do kolekcji, które w przeciwnym razie mogą zostać pominięte.
Greg Dean

1
Celem Dispose nie jest wyczyszczenie pamięci zarządzanych obiektów - takie jest zadanie śmieciarza. Chodzi o to, aby usunąć niezarządzane obiekty. Wydaje się, że istnieją dowody, że DataSets nie mają żadnych niezarządzanych odwołań, więc teoretycznie nie trzeba się ich pozbywać. To powiedziawszy, nigdy nie byłem w sytuacji, w której musiałem wychodzić z siebie, by zadzwonić Dispose - i tak bym to nazwał.
cbp

4
Pierwotne zastosowanie IDisposable jest uwolnienie niezarządzanych zasobów. Często modyfikuje też stan w sposób, który ma sens w przypadku zdanej instancji. (tj. właściwości ustawione na false, odwołania ustawione na null itp.)
Greg Dean

3
Jeśli istnieje metoda usuwania na obiekcie, została tam umieszczona z jakiegoś powodu, niezależnie od tego, czy służy ona do czyszczenia niezarządzanych obiektów, czy nie.
Chuck Conway,

4

Jeśli twoją intencją lub kontekstem tego pytania jest naprawdę wyrzucanie elementów bezużytecznych, możesz ustawić zestawy danych i zestawy danych na jawnie null lub użyć słowa kluczowego za pomocą i pozwolić im wyjść poza zakres. Dispose niewiele robi, jak powiedział wcześniej Tetraneutron. GC będzie zbierać obiekty zestawu danych, do których już nie ma odniesienia, a także te, które są poza zakresem.

Naprawdę chciałbym, aby SO zmusiło ludzi do rezygnacji z głosowania, aby napisali komentarz przed odrzuceniem odpowiedzi.


+1 Myślę, że niektórzy ludzie nie chcą pozwalać innym na rozważanie różnych punktów widzenia.
DOK

2
głosowanie w dół, w żaden sposób nie uniemożliwia ludziom rozważania różnych punktów widzenia.
Greg Dean

1

Zestawy danych implementują IDisposable dokładny MarshalByValueComponent, który implementuje IDisposable. Ponieważ zestawy danych są zarządzane, nie ma realnych korzyści z wywoływania funkcji dispose.


6
Może teraz, kto wie, co zrobi później.
Greg Dean

Takie podejście, w którym spekulujesz, że żaden kod nie zrobi tego, co powinien, jest bolesnym założeniem dla wszystkich zaangażowanych.
MicroservicesOnDDD

0

Spróbuj użyć funkcji Clear (). Działa świetnie dla mnie do usuwania.

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

0

Nie ma potrzeby Dispose (), ponieważ DataSet dziedziczy klasę MarshalByValueComponent, a MarshalByValueComponent implementuje interfejs IDisposable

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.