Widziałem kilka pytań na temat finalizatorów i IDisposable, przepełnienie stosu również powinno mieć coś na temat GC.SupressFinalize i słabe referencje
Sam Saffron
Nie sądzę, że słabe referencje mają wiele wspólnego z finalizatorami - być może powinieneś zadać bardziej bezpośrednie pytanie na ich temat.
Michael Burr
Yerp Chciałem opublikować osobne pytanie dotyczące słabych referencji, wszystko to może się ze sobą wiązać podczas budowania pul obiektów. Powinienem również zadać pytanie o odnowienie obiektu al ReRegisterForFinalize
Sam Saffron
Odpowiedzi:
296
SuppressFinalizepowinien być wywoływany tylko przez klasę, która ma finalizator. Informuje Garbage Collector (GC), że thisobiekt został w pełni wyczyszczony.
Zalecany IDisposablewzór w przypadku finalizatora to:
publicclassMyClass:IDisposable{privatebool disposed =false;protectedvirtualvoidDispose(bool disposing){if(!disposed){if(disposing){// called via myClass.Dispose(). // OK to use any private object references}// Release unmanaged resources.// Set large fields to null.
disposed =true;}}publicvoidDispose()// Implement IDisposable{Dispose(true);
GC.SuppressFinalize(this);}~MyClass()// the finalizer{Dispose(false);}}
Zwykle CLR śledzi obiekty za pomocą finalizatora podczas ich tworzenia (co powoduje, że ich tworzenie jest droższe). SuppressFinalizeinformuje GC, że obiekt został poprawnie wyczyszczony i nie musi wchodzić do kolejki finalizatora. Wygląda jak niszczyciel C ++, ale nie działa jak jeden.
SuppressFinalizeOptymalizacja nie jest trywialne, jak twoi obiekty mogą żyć długo czekać w kolejce finalizatora. Nie ulegaj pokusie, aby przywoływać SuppressFinalizeinne przedmioty. To poważna wada, która czeka.
Wytyczne projektowe informują nas, że finalizator nie jest konieczny, jeśli obiekt jest implementowany IDisposable, ale jeśli masz finalizator, powinieneś wdrożyć go, IDisposableaby umożliwić deterministyczne porządkowanie swojej klasy.
Przez większość czasu powinieneś być w stanie uciec od IDisposableczyszczenia zasobów. Finalizator powinien być potrzebny tylko wtedy, gdy obiekt utrzymuje niezarządzane zasoby i musisz zagwarantować, że te zasoby zostaną oczyszczone.
Uwaga: Czasami koderzy dodają finalizator do debugowania kompilacji własnych IDisposableklas w celu przetestowania, czy kod IDisposablepoprawnie pozbył się swojego obiektu.
publicvoidDispose()// Implement IDisposable{Dispose(true);#if DEBUG
GC.SuppressFinalize(this);#endif}#if DEBUG~MyClass()// the finalizer{Dispose(false);}#endif
W pierwszym fragmencie kodu zamieszczam tylko, jak wygląda zalecany wzorzec IDisposable + finalizatora. Kod debugujący jest dobry, ale może rozpraszać. .. Mogę tylko zalecać unikanie finalizatorów, z wyjątkiem klas, które mają niezarządzane zasoby. Pisanie bezpiecznego kodu finalizatora nie jest trywialne.
Robert Paulson
1
Cześć, Dlaczego musimy wywołać dispose z false jako parametr z finalizatora? Co się stanie, jeśli dyspozycja nigdy nie zostanie wezwana, a wtedy nie zostanie sprzedana? Co jeśli po prostu sprawdzimy, czy obiekt został zutylizowany, czy nie i wykonamy faktyczne czyszczenie.
Marzyciel
3
@Dreamer - zależy od twojej implementacji. Zasadniczo chcesz wiedzieć, czy finalizator wywołuje Dispose w porównaniu z implementacją IDisposable.Dispose (). W przypadku wywołania z finalizatora musisz założyć, że prywatne odniesienia nie są już ważne i naprawdę nie możesz wiele zrobić. Jeśli jednak zostanie wywołany z IDisposable.Dispose (), wiesz, że odwołania są nadal aktualne.
Robert Paulson,
32
Jeśli klasa implementująca IDisposablenie jest sealed, powinna zawierać wywołanie, GC.SuppressFinalize(this)nawet jeśli nie zawiera finalizatora zdefiniowanego przez użytkownika . Jest to konieczne, aby zapewnić właściwą semantykę dla typów pochodnych, które dodają finalizator zdefiniowany przez użytkownika, ale tylko zastępują chronioną Dispose(bool)metodę.
Sam Harwell
1
sealedDla klas pochodnych ważne jest, aby nie być wspomnianym przez @SamHarwell. CodeAnalysis daje ok. 1816 + ca1063, gdy klasa nie jest zapieczętowana, ale klasy zapieczętowane są w porządku bez SuppressFinalize.
dashy
38
SupressFinalizeinformuje system, że jakakolwiek praca wykonana w finalizatorze została już wykonana, więc finalizator nie musi być wywoływany. Z dokumentów .NET:
Obiekty, które implementują interfejs IDisposable, mogą wywoływać tę metodę z metody IDisposable.Dispose, aby uniemożliwić śmieciarzowi wywołanie Object.Finalizuj na obiekcie, który go nie wymaga.
Ogólnie rzecz biorąc, większość Dispose()metod powinna być w stanie wywołać GC.SupressFinalize(), ponieważ powinna wyczyścić wszystko, co zostanie wyczyszczone w finalizatorze.
SupressFinalizejest po prostu czymś, co zapewnia optymalizację, która pozwala systemowi nie zawracać sobie głowy kolejkowaniem obiektu do wątku finalizatora. Prawidłowo napisany Dispose()/ finalizator powinien działać poprawnie z lub bez połączenia z GC.SupressFinalize().
Metodę tę należy wywołać na Disposemetodzie obiektów, która implementuje IDisposable, w ten sposób GC nie wywołałby finalizatora innym razem, gdyby ktoś wywołał Disposemetodę.
Myślę, że „Must” jest błędne - nawet „nie powinno” - po prostu w niektórych scenariuszach możesz wyeliminować narzut związany z kolejkowaniem / finalizowaniem obiektu.
Podstawowy
1
Dispose(true);
GC.SuppressFinalize(this);
Jeśli obiekt ma finalizator, .net umieścił odwołanie w kolejce finalizacji.
Ponieważ mamy wywołanie Dispose(ture), jest to jasny obiekt, więc nie potrzebujemy kolejki finalizacji, aby wykonać tę pracę.
Więc wywołaj GC.SuppressFinalize(this)odwołanie odwołania w kolejce finalizacji.
Jeśli klasa, czy coś pochodzi od niego, może trzymać ostatni koncert odniesienie do obiektu z finalizatora, następnie albo GC.SuppressFinalize(this)czy GC.KeepAlive(this)należy wezwać na obiekcie po każdej operacji, które mogłyby mieć negatywny wpływ tego finalizatora, zapewniając w ten sposób, że won finalizator będzie działać do momentu zakończenia tej operacji.
Koszt GC.KeepAlive()i GC.SuppressFinalize(this)są zasadniczo takie same w każdej klasie, która nie ma finalizatora, a klasy, które mają finalizatory, powinny generalnie wywoływać GC.SuppressFinalize(this), więc użycie tej ostatniej funkcji jako ostatniego kroku Dispose()może nie zawsze być konieczne, ale nie będzie mylić się.
Odpowiedzi:
SuppressFinalize
powinien być wywoływany tylko przez klasę, która ma finalizator. Informuje Garbage Collector (GC), żethis
obiekt został w pełni wyczyszczony.Zalecany
IDisposable
wzór w przypadku finalizatora to:Zwykle CLR śledzi obiekty za pomocą finalizatora podczas ich tworzenia (co powoduje, że ich tworzenie jest droższe).
SuppressFinalize
informuje GC, że obiekt został poprawnie wyczyszczony i nie musi wchodzić do kolejki finalizatora. Wygląda jak niszczyciel C ++, ale nie działa jak jeden.SuppressFinalize
Optymalizacja nie jest trywialne, jak twoi obiekty mogą żyć długo czekać w kolejce finalizatora. Nie ulegaj pokusie, aby przywoływaćSuppressFinalize
inne przedmioty. To poważna wada, która czeka.Wytyczne projektowe informują nas, że finalizator nie jest konieczny, jeśli obiekt jest implementowany
IDisposable
, ale jeśli masz finalizator, powinieneś wdrożyć go,IDisposable
aby umożliwić deterministyczne porządkowanie swojej klasy.Przez większość czasu powinieneś być w stanie uciec od
IDisposable
czyszczenia zasobów. Finalizator powinien być potrzebny tylko wtedy, gdy obiekt utrzymuje niezarządzane zasoby i musisz zagwarantować, że te zasoby zostaną oczyszczone.Uwaga: Czasami koderzy dodają finalizator do debugowania kompilacji własnych
IDisposable
klas w celu przetestowania, czy kodIDisposable
poprawnie pozbył się swojego obiektu.źródło
IDisposable
nie jestsealed
, powinna zawierać wywołanie,GC.SuppressFinalize(this)
nawet jeśli nie zawiera finalizatora zdefiniowanego przez użytkownika . Jest to konieczne, aby zapewnić właściwą semantykę dla typów pochodnych, które dodają finalizator zdefiniowany przez użytkownika, ale tylko zastępują chronionąDispose(bool)
metodę.sealed
Dla klas pochodnych ważne jest, aby nie być wspomnianym przez @SamHarwell. CodeAnalysis daje ok. 1816 + ca1063, gdy klasa nie jest zapieczętowana, ale klasy zapieczętowane są w porządku bezSuppressFinalize
.SupressFinalize
informuje system, że jakakolwiek praca wykonana w finalizatorze została już wykonana, więc finalizator nie musi być wywoływany. Z dokumentów .NET:Ogólnie rzecz biorąc, większość
Dispose()
metod powinna być w stanie wywołaćGC.SupressFinalize()
, ponieważ powinna wyczyścić wszystko, co zostanie wyczyszczone w finalizatorze.SupressFinalize
jest po prostu czymś, co zapewnia optymalizację, która pozwala systemowi nie zawracać sobie głowy kolejkowaniem obiektu do wątku finalizatora. Prawidłowo napisanyDispose()
/ finalizator powinien działać poprawnie z lub bez połączenia zGC.SupressFinalize()
.źródło
Metodę tę należy wywołać na
Dispose
metodzie obiektów, która implementujeIDisposable
, w ten sposób GC nie wywołałby finalizatora innym razem, gdyby ktoś wywołałDispose
metodę.Patrz: Metoda GC.SuppressFinalize (Object) - Dokumenty Microsoft
źródło
Jeśli obiekt ma finalizator, .net umieścił odwołanie w kolejce finalizacji.
Ponieważ mamy wywołanie
Dispose(ture)
, jest to jasny obiekt, więc nie potrzebujemy kolejki finalizacji, aby wykonać tę pracę.Więc wywołaj
GC.SuppressFinalize(this)
odwołanie odwołania w kolejce finalizacji.źródło
Jeśli klasa, czy coś pochodzi od niego, może trzymać ostatni koncert odniesienie do obiektu z finalizatora, następnie albo
GC.SuppressFinalize(this)
czyGC.KeepAlive(this)
należy wezwać na obiekcie po każdej operacji, które mogłyby mieć negatywny wpływ tego finalizatora, zapewniając w ten sposób, że won finalizator będzie działać do momentu zakończenia tej operacji.Koszt
GC.KeepAlive()
iGC.SuppressFinalize(this)
są zasadniczo takie same w każdej klasie, która nie ma finalizatora, a klasy, które mają finalizatory, powinny generalnie wywoływaćGC.SuppressFinalize(this)
, więc użycie tej ostatniej funkcji jako ostatniego krokuDispose()
może nie zawsze być konieczne, ale nie będzie mylić się.źródło