Wiem z lektury dokumentacji Microsoft, że „podstawowe” użycieIDisposable
interfejsu jest czyszczenie niezarządzanych zasobów.
Dla mnie „niezarządzany” oznacza takie rzeczy, jak połączenia z bazą danych, gniazda, uchwyty okien itp. Ale widziałem kod, w którym Dispose()
metoda jest zaimplementowana w celu swobodnego zarządzania zasobów, co wydaje mi się zbędne, ponieważ śmieciarz powinien zająć To dla ciebie.
Na przykład:
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;
}
Moje pytanie brzmi: czy to powoduje, że pamięć śmieciarza jest wolna zużyta MyCollection
szybciej niż zwykle?
edycja : Do tej pory ludzie opublikowali kilka dobrych przykładów użycia IDisposable do czyszczenia niezarządzanych zasobów, takich jak połączenia z bazą danych i mapy bitowe. Ale załóżmy, że _theList
w powyższym kodzie zawarte miliona ciągi i chciał, aby zwolnić pamięć, że teraz , zamiast czekać na śmieciarza. Czy powyższy kod by to osiągnął?
IDisposable
nic nie zaznacza.Dispose
Metoda robi to, co musi zrobić, aby oczyścić zasoby używane przez instancji. To nie ma nic wspólnego z GC.IDisposable
. I dlatego powiedziałem, że zaakceptowana odpowiedź nie odpowiada zamierzonemu pytaniu PO (i dalszej edycji), czy IDisposable pomoże w <i> uwolnieniu pamięci </i>. PonieważIDisposable
nie ma to nic wspólnego z uwalnianiem pamięci, tylko zasoby, to jak już powiedziałeś, nie ma potrzeby ustawiania zarządzanych referencji na zero, co OP robił w swoim przykładzie. Tak więc poprawna odpowiedź na jego pytanie brzmi: „Nie, to nie pomaga zwolnić pamięci szybciej. W rzeczywistości nie pomaga w ogóle zwolnić pamięci, tylko zasoby”. Ale i tak dziękuję za Twój wkład.Odpowiedzi:
Celem Dispose jest uwolnienie niezarządzanych zasobów. W pewnym momencie należy to zrobić, w przeciwnym razie nigdy nie zostaną oczyszczone. Śmieciarka nie wie, jak wywołać
DeleteHandle()
zmienną typuIntPtr
, nie wie, czy musi zadzwonićDeleteHandle()
.Obiekt, który utworzyłeś, musi ujawnić jakąś metodę, którą świat zewnętrzny może wywołać, aby oczyścić niezarządzane zasoby. Metodę można nazwać dowolnie:
lub
Zamiast tego istnieje znormalizowana nazwa tej metody:
Utworzono nawet interfejs
IDisposable
, który ma tylko jedną metodę:Sprawiasz, że obiekt ujawnia
IDisposable
interfejs, i w ten sposób obiecujesz, że napisałeś tę pojedynczą metodę, aby oczyścić niezarządzane zasoby:I jesteś skończony. Tyle że możesz zrobić lepiej.
Co się stanie, jeśli twój obiekt przydzieli 250 MB System.Drawing.Bitmap (tj. Zarządzaną klasą .NET Bitmap) jako pewnego rodzaju bufor ramki? Jasne, jest to zarządzany obiekt .NET, a śmieciarz go zwolni. Ale czy naprawdę chcesz zostawić 250 MB pamięci po prostu tam siedząc - czekając, aż śmieciarz w końcu przyjdzie i ją zwolni? Co się stanie, jeśli istnieje otwarte połączenie z bazą danych ? Z pewnością nie chcemy, aby to połączenie było otwarte i czekało, aż GC sfinalizuje obiekt.
Jeśli użytkownik zadzwonił
Dispose()
(co oznacza, że nie planuje już używać obiektu), dlaczego nie pozbyć się marnotrawstwa map bitowych i połączeń z bazą danych?Więc teraz będziemy:
Zaktualizujmy więc naszą
Dispose()
metodę, aby pozbyć się zarządzanych obiektów:I wszystko jest dobrze, tyle że możesz zrobić lepiej !
Co się stanie, jeśli osoba zapomni zadzwonić
Dispose()
na Twój obiekt? Wtedy wyciekną niektóre niezarządzane zasoby!Jeśli osoba zapomniała zadzwonić
Dispose()
, nadal możemy uratować jej bekon! Mamy jeszcze sposób nazwać to dla nich kiedy garbage collector wreszcie trafia dokoła do uwolnienia (tj finalizowanie) Naszym celem.Zniszczenie naszego obiektu przez śmieciarza to idealny czas na uwolnienie tych nieznośnych niezarządzanych zasobów. Robimy to, zastępując
Finalize()
metodę.Ale w tym kodzie jest błąd. Widzisz, śmieciarz działa na wątku w tle ; nie znasz kolejności niszczenia dwóch obiektów. Jest całkiem możliwe, że w twoim
Dispose()
kodzie nie ma już obiektu zarządzanego , którego próbujesz się pozbyć (ponieważ chciałeś być pomocny):Potrzebny jest zatem sposób,
Finalize()
aby powiedzieć,Dispose()
że nie powinien on dotykać żadnych zarządzanych zasobów (ponieważ mogą ich nie być już ), jednocześnie uwalniając niezarządzane zasoby.Standardowym wzorcem do tego jest posiadanie
Finalize()
iDispose()
wywoływanie trzeciej (!) Metody; gdzie przekazujesz logiczne powiedzenie, jeśli dzwonisz zDispose()
(w przeciwieństwie doFinalize()
), co oznacza, że można bezpiecznie uwolnić zarządzane zasoby.Ta wewnętrzna metoda może mieć dowolną nazwę, taką jak „CoreDispose” lub „MyInternalDispose”, ale tradycja nazywa ją
Dispose(Boolean)
:Jednak bardziej pomocną nazwą parametru może być:
I zmieniasz implementację
IDisposable.Dispose()
metody na:a twój finalizator:
I wszystko jest dobrze, tyle że możesz zrobić lepiej !
Jeśli użytkownik wywoła
Dispose()
Twój obiekt, wszystko zostało wyczyszczone. Później, kiedy pojawia się moduł wyrzucający śmieci i wywołuje Finalize, zadzwoniDispose
ponownie.Jest to nie tylko marnotrawstwo, ale jeśli Twój obiekt zawiera śmieciowe odniesienia do obiektów, które zostały już usunięte po ostatnim wywołaniu
Dispose()
, spróbujesz usunąć je ponownie!Zauważysz w moim kodzie, że starałem się usunąć odwołania do obiektów, które pozbyłem się, więc nie próbuję wywoływać
Dispose
do śmieci. Ale to nie powstrzymało przedostania się subtelnego robaka.Gdy użytkownik wywołuje
Dispose()
: uchwyt CursorFileBitmapIconServiceHandle jest zniszczony. Później, gdy moduł wyrzucający śmieci uruchomi się, spróbuje ponownie zniszczyć ten sam uchwyt.Sposób, w jaki to naprawiasz, polega na powiedzeniu śmieciarzowi, że nie musi zawracać sobie głowy finalizowaniem obiektu - jego zasoby zostały już wyczyszczone i nie trzeba więcej pracować. Można to zrobić poprzez wywołanie
GC.SuppressFinalize()
wDispose()
metodzie:Teraz, gdy użytkownik zadzwonił
Dispose()
, mamy:Nie ma sensu, aby GC obsługiwał finalizator - wszystko załatwione.
Czy nie mogę użyć Finalize do czyszczenia niezarządzanych zasobów?
Dokumentacja
Object.Finalize
mówi:Ale dokumentacja MSDN mówi również
IDisposable.Dispose
:Więc co to jest? Które jest miejscem, w którym mogę uporządkować niezarządzane zasoby? Odpowiedź to:
Z pewnością możesz umieścić swoje niezarządzane porządki w finalizatorze:
Problem polega na tym, że nie masz pojęcia, kiedy śmieciarz podejdzie do sfinalizowania obiektu. Twoje un zarządzane, UN, UN potrzebne wykorzystywane rodzime zasoby będą trzymać się aż do garbage collector ostatecznie skończy. Następnie wywoła metodę finalizatora; czyszczenie niezarządzanych zasobów. Dokumentacja Object.Finalize wskazuje na to:
Jest to zaleta używania
Dispose
do czyszczenia niezarządzanych zasobów; poznajesz i kontrolujesz, kiedy niezarządzane zasoby są usuwane. Ich zniszczenie jest „deterministyczne” .Aby odpowiedzieć na twoje pierwotne pytanie: dlaczego nie zwolnić pamięci teraz, zamiast na to, kiedy GC zdecyduje się to zrobić? Mam twarzy oprogramowania do rozpoznawania, że potrzeby , aby pozbyć się z 530 MB wewnętrznej obrazami teraz , ponieważ nie jesteś już potrzebny. Kiedy tego nie robimy: maszyna zgrzyta do zatrzymania.
Czytanie bonusowe
Dla każdego, kto lubi styl tej odpowiedzi (wyjaśniając dlaczego , więc jak staje się oczywiste), sugeruję przeczytanie pierwszego rozdziału Essential COM Dona Boxa:
Na 35 stronach wyjaśnia problemy związane z używaniem obiektów binarnych i wymyśla COM na twoich oczach. Kiedy zrozumiesz, dlaczego COM, pozostałe 300 stron jest oczywistych i opisuje szczegółowo implementację Microsoftu.
Myślę, że każdy programista, który kiedykolwiek miał do czynienia z obiektami lub COM, powinien przynajmniej przeczytać pierwszy rozdział. To najlepsze wytłumaczenie czegokolwiek w historii.
Dodatkowe czytanie bonusowe
Kiedy wszystko, co wiesz, jest złe przez Erica Lipperta
źródło
null
. Po pierwsze oznacza to, że nie możesz ich wykonaćreadonly
, a po drugie, musisz zrobić bardzo brzydkie!=null
kontrole (jak w przykładowym kodzie). Możesz mieć flagędisposed
, ale łatwiej się tym nie przejmować. .NET GC jest na tyle agresywny, że odniesienie do polax
nie będzie już liczone jako „używane” do momentu przekroczeniax.Dispose()
linii.IDisposable
jest często używany do wykorzystaniausing
instrukcji i skorzystania z łatwego sposobu przeprowadzenia deterministycznego czyszczenia zarządzanych obiektów.źródło
Wzorzec Dispose ma na celu zapewnienie mechanizmu czyszczenia zasobów zarządzanych i niezarządzanych, a kiedy to nastąpi, zależy od tego, jak wywoływana jest metoda Dispose. W twoim przykładzie użycie Dispose w rzeczywistości nie robi nic związanego z usuwaniem, ponieważ wyczyszczenie listy nie ma wpływu na usuwanie tej kolekcji. Podobnie, wezwania do ustawienia zmiennych na null również nie mają wpływu na GC.
Możesz zapoznać się z tym artykułem, aby uzyskać więcej informacji na temat wdrażania wzorca Dispose, ale w zasadzie wygląda to tak:
Najważniejszą metodą jest tutaj Dispose (bool), który faktycznie działa w dwóch różnych okolicznościach:
Problem z po prostu pozwoleniem GC na wykonanie czyszczenia polega na tym, że nie masz rzeczywistej kontroli nad tym, kiedy GC uruchomi cykl zbierania (możesz wywołać GC.Collect (), ale tak naprawdę nie powinieneś), aby zasoby mogły pozostać około dłużej niż potrzeba. Pamiętaj, że wywołanie Dispose () tak naprawdę nie powoduje cyklu gromadzenia ani w żaden sposób nie powoduje, że GC zbiera / uwalnia obiekt; po prostu zapewnia środki do bardziej deterministycznego czyszczenia używanych zasobów i informuje GC, że to czyszczenie zostało już wykonane.
Cały sens IDisposable i wzorca usuwania nie polega na natychmiastowym uwolnieniu pamięci. Jedynym przypadkiem, gdy wezwanie do Dispose będzie miało nawet szansę natychmiastowego zwolnienia pamięci, jest wtedy, gdy obsługuje on == fałszywy scenariusz i manipuluje niezarządzanymi zasobami. W przypadku kodu zarządzanego pamięć nie zostanie faktycznie odzyskana, dopóki GC nie uruchomi cyklu gromadzenia, nad którym tak naprawdę nie masz kontroli (poza wywoływaniem GC.Collect (), o którym już wspomniałem, nie jest dobrym pomysłem).
Twój scenariusz nie jest tak naprawdę prawidłowy, ponieważ ciągi w .NET nie używają żadnych niezmienionych zasobów i nie implementują IDisposable, nie ma sposobu, aby zmusić je do „wyczyszczenia”.
źródło
Po wywołaniu Dispose nie powinno być żadnych dalszych wywołań metod obiektu (chociaż obiekt powinien tolerować dalsze wywołania Dispose). Dlatego przykład w pytaniu jest głupi. Jeśli wywołane zostanie Dispose, sam obiekt można odrzucić. Dlatego użytkownik powinien po prostu odrzucić wszystkie odwołania do tego całego obiektu (ustawić je na wartość NULL), a wszystkie powiązane z nim obiekty automatycznie zostaną oczyszczone.
Jeśli chodzi o ogólne pytanie dotyczące zarządzanego / niezarządzanego i dyskusję w innych odpowiedziach, myślę, że każda odpowiedź na to pytanie musi zaczynać się od definicji niezarządzanego zasobu.
Sprowadza się do tego, że istnieje funkcja, którą można wywołać, aby wprowadzić system w stan, a także inną funkcję, którą można wywołać, aby przywrócić go z tego stanu. Teraz, w typowym przykładzie, pierwszy może być funkcją, która zwraca uchwyt pliku, a drugi może być wywołaniem
CloseHandle
.Ale - i to jest klucz - mogą to być dowolne pasujące pary funkcji. Jeden buduje stan, drugi go niszczy. Jeśli stan został zbudowany, ale jeszcze nie zburzony, istnieje instancja zasobu. Musisz zorganizować porzucenie we właściwym czasie - zasoby nie są zarządzane przez CLR. Jedynym automatycznie zarządzanym typem zasobu jest pamięć. Istnieją dwa rodzaje: GC i stos. Rodzaje wartości są zarządzane przez stos (lub poprzez zaczepienie przejażdżki wewnątrz typów referencyjnych), a typy referencyjne są zarządzane przez GC.
Funkcje te mogą powodować zmiany stanu, które można dowolnie przeplatać lub mogą wymagać perfekcyjnego zagnieżdżenia. Zmiany stanu mogą być bezpieczne dla wątków lub nie.
Spójrz na przykład w pytaniu Sprawiedliwości. Zmiany w wcięciach pliku dziennika muszą być idealnie zagnieżdżone, w przeciwnym razie wszystko pójdzie nie tak. Nie są też prawdopodobnie bezpieczne dla wątków.
Możliwe jest połączenie się ze śmieciarzem, aby oczyścić niezarządzane zasoby. Ale tylko wtedy, gdy funkcje zmiany stanu są bezpieczne dla wątków, a dwa stany mogą mieć okresy życia, które nakładają się w jakikolwiek sposób. Zatem przykład zasobu Sprawiedliwości NIE może mieć finalizatora! To po prostu nikomu nie pomogłoby.
W przypadku tego rodzaju zasobów można po prostu wdrożyć
IDisposable
, bez finalizatora. Finalizator jest absolutnie opcjonalny - tak musi być. Zostało to zlekceważone lub nawet nie wspomniane w wielu książkach.Następnie musisz użyć tego
using
oświadczenia, aby mieć szansę na sprawdzenie, czyDispose
zostanie wywołane. Zasadniczo jest to podobne do zaczepienia się na stosie (tak jak finalizator do GC,using
do stosu).Brakująca część polega na tym, że musisz ręcznie napisać Dispose i wywołać ją na swoje pola i klasę podstawową. Programiści C ++ / CLI nie muszą tego robić. Kompilator zapisuje je w większości przypadków.
Istnieje alternatywa, którą preferuję dla stanów, które idealnie się zagnieżdżają i nie są bezpieczne dla wątków (oprócz wszystkiego innego, unikanie IDisposable pozwala uniknąć kłótni z kimś, kto nie może się oprzeć dodaniu finalizatora do każdej klasy, która implementuje IDisposable) .
Zamiast pisać klasę, piszesz funkcję. Funkcja akceptuje uczestnika, aby oddzwonił do:
A potem prosty przykład to:
Przekazana lambda służy jako blok kodu, więc to tak, jakbyś stworzył własną strukturę kontrolną, która służy temu samemu celowi
using
, z tym wyjątkiem, że nie istnieje już ryzyko, że osoba dzwoniąca nadużyje jej. Nie ma mowy, żeby nie udało się wyczyścić zasobu.Ta technika jest mniej przydatna, jeśli zasób może mieć nakładające się okresy istnienia, ponieważ wtedy chcesz być w stanie zbudować zasób A, następnie zasób B, następnie zabić zasób A, a następnie zabić zasób B. jeśli zmusiłeś użytkownika do idealnego zagnieżdżenia w ten sposób. Ale musisz użyć
IDisposable
(ale nadal bez finalizatora, chyba że wdrożyłeś wątkowe bezpieczeństwo, które nie jest darmowe).źródło
enter
iexit
jest rdzeniem tego, jak myślę o zasobie. Subskrybowanie / wypisywanie się z wydarzeń powinno się z tym zmieścić bez trudności. Pod względem cech ortogonalnych / zamiennych jest praktycznie nie do odróżnienia od wycieku pamięci. (Nie jest to zaskakujące, ponieważ subskrypcja po prostu dodaje obiekty do listy.)Scenariusze Korzystam z IDisposable: oczyszczaj niezarządzane zasoby, anuluj subskrypcję zdarzeń, zamykaj połączenia
Idiom, którego używam do implementacji IDisposable ( nie jest wątkowo bezpieczny ):
źródło
Tak, ten kod jest całkowicie zbędny i niepotrzebny i nie zmusza śmieciarza do robienia czegokolwiek, czego inaczej by nie zrobił (gdy wystąpienie MyCollection wykracza poza zakres, to znaczy.) Zwłaszcza
.Clear()
wywołania.Odpowiedz na swoją edycję: Sortuj. Jeśli to zrobię:
Jest funkcjonalnie identyczny z tym dla celów zarządzania pamięcią:
Jeśli naprawdę naprawdę chcesz natychmiast zwolnić pamięć, zadzwoń
GC.Collect()
. Jednak nie ma powodu, aby to robić. Pamięć zostanie zwolniona, gdy będzie potrzebna.źródło
Jeśli
MyCollection
i tak będzie zbierany śmieci, nie powinieneś go usuwać. Spowoduje to po prostu wyrzucenie procesora więcej niż to konieczne, a nawet może unieważnić niektóre wstępnie obliczone analizy, które moduł śmieciowy już wykonał.Zwykle
IDisposable
robię takie rzeczy, jak zapewnienie prawidłowego usuwania wątków wraz z niezarządzanymi zasobami.EDYCJA W odpowiedzi na komentarz Scotta:
Koncepcyjnie, GC utrzymuje widok wykresu odniesienia do obiektu i wszystkich odniesień do niego z ramek stosu wątków. Ta sterta może być dość duża i obejmować wiele stron pamięci. Jako optymalizacja, GC buforuje swoją analizę stron, które prawdopodobnie nie zmienią się bardzo często, aby uniknąć niepotrzebnego ponownego skanowania strony. GC otrzymuje powiadomienie z jądra, gdy dane na stronie się zmieniają, więc wie, że strona jest brudna i wymaga ponownego skanowania. Jeśli kolekcja znajduje się w Gen0, prawdopodobnie inne rzeczy na stronie również się zmieniają, ale jest to mniej prawdopodobne w Gen1 i Gen2. Anegdotycznie te haki nie były dostępne w Mac OS X dla zespołu, który przeniósł GC na Maca, aby wtyczka Silverlight działała na tej platformie.
Kolejny punkt przeciwko niepotrzebnemu usuwaniu zasobów: wyobraź sobie sytuację, w której proces jest rozładowywany. Wyobraź sobie również, że proces działa już od pewnego czasu. Istnieje prawdopodobieństwo, że wiele stron pamięci tego procesu zostało zamienionych na dysk. Przynajmniej nie są już w pamięci podręcznej L1 lub L2. W takiej sytuacji nie ma sensu, aby aplikacja, która się rozładowuje, zamieniała wszystkie dane i strony kodowe z powrotem do pamięci w celu „zwolnienia” zasobów, które zostaną zwolnione przez system operacyjny i tak po zakończeniu procesu. Dotyczy to zarządzanych, a nawet niektórych niezarządzanych zasobów. Tylko zasoby, które utrzymują przy życiu wątki inne niż tło, muszą zostać usunięte, w przeciwnym razie proces pozostanie przy życiu.
Teraz podczas normalnego wykonywania istnieją efemeryczne zasoby, które należy poprawnie wyczyścić (ponieważ @ fezmonkey wskazuje połączenia z bazą danych, gniazda, uchwyty okien ), aby uniknąć wycieków pamięci niezarządzanych. To są rzeczy, które należy usunąć. Jeśli utworzysz jakąś klasę, która posiada wątek (a przez własność rozumiem, że go utworzyła i dlatego jest odpowiedzialna za zapewnienie, że przestanie, przynajmniej przez mój styl kodowania), wtedy ta klasa najprawdopodobniej musi zaimplementować
IDisposable
i zerwać wątek podczasDispose
..NET Framework wykorzystuje
IDisposable
interfejs jako sygnał, a nawet ostrzeżenie dla programistów, że ta klasa musi zostać usunięta. Nie mogę wymyślić żadnych typów w ramach, które implementująIDisposable
(z wyjątkiem jawnych implementacji interfejsu), w których usuwanie jest opcjonalne.źródło
Dispose()
połączeń opcjonalnych patrz: stackoverflow.com/questions/913228/...W opublikowanym przykładzie nadal nie „zwalnia ona teraz pamięci”. Cała pamięć jest gromadzona w pamięci, ale może umożliwić gromadzenie pamięci we wcześniejszej generacji . Aby się upewnić, musiałbyś przeprowadzić kilka testów.
Wytyczne dotyczące projektowania ram są wytycznymi, a nie regułami. Mówią, do czego służy interfejs, kiedy go używać, jak go używać, a kiedy go nie używać.
Kiedyś czytałem kod, który był prostym RollBack () w przypadku niepowodzenia przy użyciu IDisposable. Poniższa klasa MiniTx sprawdziłaby flagę na Dispose (), a gdyby
Commit
wywołanie nigdy się nie wydarzyło, wywołałobyRollback
się samo. Dodano warstwę pośrednią, dzięki czemu kod wywołujący jest znacznie łatwiejszy do zrozumienia i utrzymania. Wynik wyglądał mniej więcej tak:Widziałem też kod czasowy / rejestrujący, który robi to samo. W tym przypadku metoda Dispose () zatrzymała stoper i zarejestrowała wyjście z bloku.
Oto kilka konkretnych przykładów, które nie wykonują żadnego niezarządzanego czyszczenia zasobów, ale z powodzeniem używają IDisposable do tworzenia czystszego kodu.
źródło
Jeśli chcesz teraz usunąć , użyj niezarządzanej pamięci .
Widzieć:
źródło
Nie będę powtarzał zwykłych rzeczy na temat używania lub zwalniania niezarządzanych zasobów, które zostały już omówione. Chciałbym jednak zwrócić uwagę na to, co wydaje się powszechnym nieporozumieniem.
Biorąc pod uwagę następujący kod
Zdaję sobie sprawę, że implementacja jednorazowego użytku nie jest zgodna z aktualnymi wytycznymi, ale mam nadzieję, że wszyscy to rozumiecie.
Teraz, kiedy zostanie wywołane Dispose, ile pamięci zostanie zwolnione?
Odpowiedź: brak.
Wywołanie Dispose może uwolnić niezarządzane zasoby, NIE MOŻE odzyskać pamięci zarządzanej, tylko GC może to zrobić. To nie znaczy, że powyższe nie jest dobrym pomysłem, przestrzeganie powyższego wzoru jest w rzeczywistości dobrym pomysłem. Po uruchomieniu Dispose nic nie stoi na przeszkodzie, aby GC odzyskał pamięć używaną przez _Large, nawet jeśli wystąpienie LargeStuff może być nadal w zasięgu. Ciągi w _Large mogą być również w gen 0, ale wystąpienie LargeStuff może być gen 2, więc ponownie pamięć zostanie ponownie odebrana wcześniej.
Nie ma jednak sensu dodawanie finalizatora do wywołania metody Dispose pokazanej powyżej. Opóźni to ponowne zażądanie pamięci, aby umożliwić działanie finalizatora.
źródło
LargeStuff
istnieje wystarczająco długo, aby przejść do Generacji 2, i jeśli_Large
zawiera odwołanie do nowo utworzonego ciągu znaków, który znajduje się w Generacji 0, to jeśli wystąpienieLargeStuff
jest porzucone bez zerowania_Large
, wówczas ciąg, do którego odwołuje się_Large
będą przechowywane do następnej kolekcji Gen2. Zerowanie_Large
może pozwolić na wyeliminowanie ciągu przy następnej kolekcji Gen0. W większości przypadków wyzerowanie referencji nie jest pomocne, ale są przypadki, w których może przynieść pewne korzyści.Oprócz swojej podstawowej stosowania jako sposób kontrolowania życia z zasobów systemowych (całkowicie pokryte przez awesome odpowiedź z Ianem , sława!), Przy czym IDisposable / używając combo mogą być również wykorzystywane do zakresu zmiany stanu (kryzysowych) zasobów globalnych : konsola , że nici The proces , każdy globalny obiekt jak instancji aplikacji .
Napisałem artykuł o tym wzorze: http://pragmateek.com/c-scope-your-global-state-changes-with-idisposable-and-the-using-statement/
Ilustruje, w jaki sposób można chronić często używany stan globalny w sposób wielokrotnego użytku i czytelny : kolory konsoli , bieżąca kultura wątków , właściwości obiektu aplikacji Excel ...
źródło
Jeśli cokolwiek, spodziewam się, że kod będzie mniej wydajny niż przy jego pomijaniu.
Wywołanie metod Clear () jest niepotrzebne, a GC prawdopodobnie nie zrobiłby tego, gdyby Dispose tego nie zrobił ...
źródło
W
Dispose()
przykładowym kodzie są rzeczy, które robi operacja, które mogą wywołać efekt, który nie wystąpiłby z powodu normalnego GCMyCollection
obiektu.Jeśli do obiektów, do których odwołują się
_theList
lub_theDict
do których odnoszą się inne obiekty, wówczas przedmiotList<>
lubDictionary<>
obiekt nie będzie podlegał kolekcji, ale nagle nie będzie miał żadnej zawartości. Gdyby nie było operacji Dispose () jak w przykładzie, te kolekcje nadal zawierałyby swoją zawartość.Oczywiście, gdyby tak było, nazwałbym to zepsutym projektem - po prostu wskazuję (pedantycznie, jak sądzę), że
Dispose()
operacja może nie być całkowicie zbędna, w zależności od tego, czy istnieją inne zastosowania,List<>
czyDictionary<>
nie pokazane we fragmencie.źródło
Jednym z problemów większości dyskusji na temat „niezarządzanych zasobów” jest to, że tak naprawdę nie definiują tego terminu, ale wydają się sugerować, że ma to coś wspólnego z niezarządzanym kodem. Chociaż prawdą jest, że wiele rodzajów niezarządzanych zasobów współpracuje z niezarządzanym kodem, myślenie o niezarządzanych zasobach w takich kategoriach nie jest pomocne.
Zamiast tego należy rozpoznać, co łączy wszystkie zarządzane zasoby: wszystkie pociągają za sobą obiekt proszący jakąś zewnętrzną „rzecz” o zrobienie czegoś w jej imieniu, ze szkodą dla innych „rzeczy”, a druga jednostka zgadza się to zrobić, dopóki dalsze powiadomienie. Gdyby obiekt został porzucony i zniknął bez śladu, nic nie powiedziałoby temu zewnętrznemu „rzeczowi”, że nie musi już zmieniać swojego zachowania w imieniu obiektu, który już nie istnieje; w konsekwencji użyteczność rzeczy zostałaby trwale zmniejszona.
Zasób niezarządzany reprezentuje zatem zgodę jakiejś zewnętrznej „rzeczy” na zmianę jego zachowania w imieniu obiektu, co bezużyteczne pogorszyłoby użyteczność tej zewnętrznej „rzeczy”, gdyby obiekt został porzucony i przestał istnieć. Zasób zarządzany to obiekt, który jest beneficjentem takiej umowy, ale który podpisał się, aby otrzymywać powiadomienia, jeśli zostanie porzucony, i który wykorzysta takie powiadomienie, aby uporządkować swoje sprawy przed jego zniszczeniem.
źródło
IDisposable
jest dobry do rezygnacji z subskrypcji wydarzeń.źródło
Pierwsza z definicji. Dla mnie niezarządzany zasób oznacza pewną klasę, która implementuje interfejs IDisposable lub coś stworzonego przy użyciu wywołań dll. GC nie wie, jak radzić sobie z takimi obiektami. Jeśli klasa ma na przykład tylko typy wartości, nie uważam tej klasy za klasę z niezarządzanymi zasobami. W moim kodzie stosuję kolejne praktyki:
Poniższy szablon demonstruje to, co opisałem słowami jako próbkę kodu:
źródło
is IDisposable
sam powinien być uważany za niezarządzany zasób? To nie wydaje się poprawne. Również jeśli typ implementacji jest typem czystej wartości, wydaje się sugerować, że nie trzeba go usuwać. To też wydaje się złe.Podany przykładowy kod nie jest dobrym przykładem
IDisposable
użycia. Czyszczenie słownika normalnie nie powinno iść doDispose
metody. Elementy słownika zostaną usunięte i usunięte, gdy wykroczą poza zakres.IDisposable
implementacja jest wymagana, aby zwolnić niektóre pamięci / moduły obsługi, które nie zostaną zwolnione / zwolnione nawet po wyjściu poza zakres.Poniższy przykład pokazuje dobry przykład wzoru IDisposable z pewnym kodem i komentarzami.
źródło
Najbardziej uzasadnionym przypadkiem wykorzystania do rozporządzania zarządzanymi zasobami jest przygotowanie GC do odzyskania zasobów, które w przeciwnym razie nigdy nie zostałyby zebrane.
Najlepszym przykładem są odwołania cykliczne.
Chociaż najlepszą praktyką jest stosowanie wzorców, które unikają odwołań cyklicznych, jeśli w końcu otrzymamy (na przykład) obiekt „podrzędny”, który ma odwołanie z powrotem do swojego „obiektu nadrzędnego”, może to zatrzymać gromadzenie GC elementu nadrzędnego, jeśli po prostu porzucisz referencja i polegaj na GC - a jeśli już zaimplementowałeś finalizator, nigdy nie zostanie on wywołany.
Jedynym sposobem na obejście tego problemu jest ręczne przerwanie odwołań cyklicznych poprzez ustawienie odniesienia potomnego na wartości zerowe dla dzieci.
Wdrażanie IDisposable u rodziców i dzieci jest najlepszym sposobem na to. Gdy wywołanie Dispose zostanie wywołane na obiekcie Parent, wywołanie Dispose na wszystkich elementach podrzędnych, aw metodzie potomnej Dispose ustaw wartość referencji elementu nadrzędnego na null.
źródło
WeakReference
, system sprawdzi flagę wskazującą, że w ostatnim cyklu GC znaleziono referencję zrootowaną na żywo i doda obiekt do kolejki obiektów wymagających natychmiastowej finalizacji, zwolni obiekt ze sterty dużych obiektów lub unieważni słabe odniesienie. Refleksje cykliczne nie utrzymują obiektów przy życiu, jeśli nie istnieją żadne inne referencje.Widzę, że wiele odpowiedzi przesunęło się, aby mówić o używaniu IDisposable zarówno dla zasobów zarządzanych, jak i niezarządzanych. Sugeruję ten artykuł jako jedno z najlepszych wyjaśnień, jakie znalazłem dla tego, jak właściwie należy używać IDisposable.
https://www.codeproject.com/Articles/29534/IDisposable-What-Your-Mother-Never-Told-You-About
Dla rzeczywistego pytania; jeśli używasz IDisposable do czyszczenia zarządzanych obiektów, które zajmują dużo pamięci, krótka odpowiedź brzmiałaby „ nie” . Powodem jest to, że po usunięciu przedmiotu IDisposable powinieneś pozwolić mu wyjść poza zakres. W tym momencie wszelkie obiekty potomne, do których istnieją odniesienia, również są poza zakresem i zostaną zebrane.
Jedynym prawdziwym wyjątkiem jest sytuacja, gdy masz dużo pamięci związanej z obiektami zarządzanymi i blokujesz ten wątek, czekając na zakończenie jakiejś operacji. Jeśli te obiekty, które nie będą potrzebne po zakończeniu tego wywołania, ustawienie tych odwołań na wartość NULL może umożliwić modułowi odśmiecania gromadzenie ich wcześniej. Ale ten scenariusz reprezentowałby zły kod, który musiał zostać zrefaktoryzowany - nie przypadek użycia IDisposable.
źródło