Osobiście nie spotkałem się z sytuacją, w której musiałem używać typu WeakReference w .Net, ale wydaje się, że popularne jest to, że powinno się go używać w pamięci podręcznej. Dr Jon Harrop w swojej odpowiedzi na to pytanie bardzo dobrze uzasadnił użycie WeakReferences w pamięci podręcznej .
Często słyszałem też, jak programiści AS3 mówią o używaniu słabych referencji, aby zaoszczędzić na pamięci, ale na podstawie przeprowadzonych rozmów wydaje się, że zwiększają złożoność, niekoniecznie osiągając zamierzony cel, a zachowanie w czasie wykonywania jest raczej nieprzewidywalne. Do tego stopnia, że wielu po prostu się poddaje i zamiast tego ostrożniej zarządza zużyciem pamięci / optymalizuje kod, aby mniej obciążał pamięć (lub zmniejszał liczbę cykli procesora i zajmował mniej miejsca).
Dr Jon Harrop zwrócił także uwagę w swojej odpowiedzi, że słabe referencje .Net nie są miękkie, a na gen0 istnieje agresywny zbiór słabych referencji. Według MSDN długie słabe referencje dają możliwość odtworzenia obiektu but the state of the object remains unpredictable.
!
Biorąc pod uwagę te cechy, nie mogę wymyślić sytuacji, w której przydatne byłyby słabe referencje, może ktoś mógłby mnie oświecić?
źródło
Odpowiedzi:
Znalazłem uzasadnione praktyczne zastosowania słabych referencji w następujących trzech rzeczywistych sytuacjach, które faktycznie mi się przydarzyły:
Aplikacja 1: Procedury obsługi zdarzeń
Jesteś przedsiębiorcą Twoja firma sprzedaje kontrolę iskierników dla WPF. Sprzedaż jest świetna, ale zabijają cię koszty wsparcia. Zbyt wielu klientów narzeka na zawieszenie procesora i wycieki pamięci, gdy przewijają ekrany pełne iskier. Problem polega na tym, że ich aplikacja tworzy nowe linie iskrowe, gdy pojawiają się w widoku, ale wiązanie danych zapobiega gromadzeniu śmieci przez stare. Co robisz?
Wprowadź słabe odniesienie między powiązaniem danych a formantem, aby samo powiązanie danych nie zapobiegało gromadzeniu elementów bezużytecznych. Następnie dodaj do swojej kontrolera finalizator, który zrywa powiązanie danych po ich zebraniu.
Aplikacja 2: Zmienne wykresy
Jesteś następnym Johnem Carmackiem. Wynalazłeś genialną, opartą na grafie reprezentację hierarchicznych powierzchni podziału, dzięki której gry Tima Sweeneya wyglądają jak Nintendo Wii. Oczywiście nie powiem ci dokładnie, jak to działa, ale wszystko koncentruje się na tym zmiennym wykresie, na którym można znaleźć sąsiadów wierzchołka
Dictionary<Vertex, SortedSet<Vertex>>
. Topologia wykresu zmienia się wraz z bieganiem gracza. Jest tylko jeden problem: twoja struktura danych zrzuca nieosiągalne subgrafy podczas działania i musisz je usunąć, inaczej wyciek pamięci. Na szczęście jesteś geniuszem, więc wiesz, że istnieje klasa algorytmów specjalnie zaprojektowanych do lokalizowania i gromadzenia nieosiągalnych subgrafów: śmieciarki! Przeczytałeś doskonałą monografię Richarda Jonesa na ten tematale wprawia Cię w zakłopotanie i niepokój o zbliżający się termin. Co robisz?Po prostu zamieniając swój
Dictionary
na słaby stół mieszający, możesz nałożyć na siebie istniejący GC i automatycznie zbierać dla ciebie nieosiągalne podgrupy! Powrót do przeglądania reklam Ferrari.Aplikacja 3: Dekorowanie drzew
Zwisasz z sufitu cyklicznego pokoju przy klawiaturze. Masz 60 sekund na przeszukanie DUŻYCH DANYCH, zanim ktoś Cię znajdzie. Przyszedłeś przygotowany z pięknym parserem opartym na strumieniu, który polega na GC do zbierania fragmentów AST po ich przeanalizowaniu. Ale zdajesz sobie sprawę, że potrzebujesz dodatkowych metadanych na każdym AST
Node
i potrzebujesz go szybko. Co robisz?Możesz użyć a
Dictionary<Node, Metadata>
do powiązania metadanych z każdym węzłem, ale jeśli go nie wyczyścisz, silne odwołania ze słownika do starych węzłów AST utrzymają je przy życiu i spowodują wyciek pamięci. Rozwiązaniem jest słaba tablica skrótów, która utrzymuje tylko słabe odwołania do kluczy, a śmieci zbierają powiązania klucz-wartość, gdy klucz staje się nieosiągalny. Następnie, gdy węzły AST stają się nieosiągalne, są one odśmiecane, a ich powiązanie klucz-wartość jest usuwane ze słownika, pozostawiając odpowiednie metadane nieosiągalne, więc również zostaje zebrane. Następnie wszystko, co musisz zrobić po zakończeniu głównej pętli, to wślizgnąć się z powrotem przez otwór wentylacyjny, pamiętając o wymianie, gdy tylko pojawi się ochroniarz.Zauważ, że we wszystkich trzech rzeczywistych aplikacjach, które mi się przydarzyły, chciałem, aby GC zbierała tak agresywnie, jak to możliwe. Dlatego są to legalne aplikacje. Wszyscy inni się mylą.
źródło
ConditionalWeakTable
w .NET).ConditionalWeakTable
tego właśnie użyją aplikacje 2 i 3, podczas gdy niektóre osoby na innych postach faktycznie używająDictionary<WeakReference, T>
. Nie mam pojęcia, dlaczego - zawsze otrzymujesz tonę zerowąWeakReference
z wartościami, do których żaden klucz nie może uzyskać dostępu, niezależnie od tego, jak to zrobisz. Ridik.Dokument Microsoft Słabe wzorce zdarzeń .
źródło
Pozwól, że postawię to na pierwszym miejscu i wrócę do tego:
Zacznijmy od początku:
- przepraszam z góry za niezamierzone przestępstwo, ale wrócę na chwilę do poziomu „Dicka i Jane”, ponieważ nigdy nie można powiedzieć publiczności.
Więc kiedy masz obiekt
X
- określmy go jako instancjęclass Foo
- NIE MOŻE on żyć sam (głównie prawda); W ten sam sposób, w jaki „Żaden człowiek nie jest wyspą”, istnieje tylko kilka sposobów, w jakie obiekt może awansować do Islandhood - chociaż nazywa się to korzeniem GC w CLR. Bycie rootem GC lub posiadanie ustalonego łańcucha połączeń / odniesień do roota GC jest w zasadzie tym, co decyduje o tym, czyFoo x = new Foo()
śmieci są zbierane, czy nie .Jeśli nie możesz wrócić do korzenia GC ani przez stertę, ani przez stos, jesteś skutecznie osierocony i prawdopodobnie zostaniesz oznaczony / zebrany w następnym cyklu.
W tym miejscu spójrzmy na kilka okropnie wymyślonych przykładów:
Po pierwsze
Foo
:Dość proste - nie jest bezpieczne dla wątków, więc nie próbuj tego, ale zachowuje przybliżoną „liczbę referencji” aktywnych instancji i dekrecji po ich sfinalizowaniu.
Teraz spójrzmy na
FooConsumer
:Mamy więc obiekt, który jest już własnym katalogiem głównym GC (cóż ... konkretnie, zostanie zrootowany przez łańcuch bezpośrednio do domeny aplikacji, w której działa ta aplikacja, ale to inny temat), który ma dwie metody zaczepienia się na
Foo
instancji - przetestujmy to:Czy w związku z powyższym spodziewałbyś się, że obiekt, o którym kiedyś była mowa,
f
będzie „kolekcjonerski”?Nie, ponieważ istnieje inny obiekt, który teraz zawiera odniesienie do niego -
Dictionary
w tejSingleton
instancji statycznej.Ok, spróbujmy słabego podejścia:
Teraz, kiedy mamy walnięcie nasze odniesienie do dokonywanych poza rynkiem
Foo
-to-było-raz w miesiącuf
, nie nie są bardziej „twarde” odniesienia do obiektu, więc jest to dla kolekcjonerów - wWeakReference
stworzony przez słabego słuchacza nie będzie temu zapobiec.Dobre przypadki użycia:
Procedury obsługi zdarzeń (chociaż najpierw przeczytaj: Słabe zdarzenia w C # )
Masz sytuację, w której spowodowałbyś „odwołanie rekurencyjne” (tj. Obiekt A odnosi się do obiektu B, który odnosi się do obiektu A, zwanego również „przeciekiem pamięci”)(edytuj: derp, oczywiście, że to nie jest to prawda)Chcesz „nadawać” coś do zbioru obiektów, ale nie chcesz być tym, co utrzymuje je przy życiu; a
List<WeakReference>
można łatwo utrzymać, a nawet przyciąć, usuwając gdzieref.Target == null
źródło
Jak logiczne wycieki, które są naprawdę trudne do wyśledzenia, podczas gdy użytkownicy po prostu zauważają, że uruchamianie oprogramowania przez długi czas ma tendencję do zajmowania coraz większej ilości pamięci i spowalniania aż do ponownego uruchomienia? Ja nie.
Zastanów się, co się stanie, jeśli użytkownik, który zażąda usunięcia zasobu aplikacji powyżej,
Thing2
nie poradzi sobie odpowiednio z takim zdarzeniem w ramach:... i pod którym jednym z takich błędów prawdopodobnie zostanie złapany podczas testowania, a który nie i nie poleciałby pod radarem jak błąd myśliwca ukrycia. Współwłasność jest najczęściej bezsensownym pomysłem.
źródło
Bardzo ilustrującym przykładem słabych odniesień zastosowanych z dobrym skutkiem jest ConditionalWeakTable , która jest używana przez DLR (między innymi) do dołączania dodatkowych „elementów” do obiektów.
Nie chcesz, aby stół utrzymywał obiekt przy życiu. Ta koncepcja po prostu nie mogłaby działać bez słabych referencji.
Wydaje mi się jednak, że wszystkie zastosowania słabych referencji pojawiły się długo po ich dodaniu do języka, ponieważ słabe referencje są częścią .NET od wersji 1.1. Wygląda na to, że chciałbyś coś dodać, aby brak deterministycznego zniszczenia nie cofnął cię w kąt, jeśli chodzi o funkcje językowe.
źródło
WeakReference
typu, ponieważ sytuacja jest znacznie bardziej złożona. Wykorzystuje różne funkcje udostępniane przez CLR.Jeśli masz warstwę pamięci podręcznej zaimplementowaną w języku C #, znacznie lepiej jest umieścić dane w pamięci podręcznej jako słabe odwołania, może to pomóc w poprawie wydajności warstwy pamięci podręcznej.
Pomyśl, że to podejście można również zastosować do implementacji sesji. Ponieważ sesja jest przez długi czas przedmiotem życia, może to być przypadek, gdy nie ma pamięci dla nowego użytkownika. W takim przypadku znacznie lepiej będzie usunąć jakiś obiekt sesji użytkownika, a następnie zgłosić wyjątek OutOfMemoryException.
Ponadto, jeśli masz duży obiekt w aplikacji (jakaś duża tabela odnośników itp.), Należy go używać dość rzadko, a odtworzenie takiego obiektu nie jest bardzo kosztowną procedurą. Więc lepiej, aby to było jak tydzień odniesienia, aby mieć sposób na uwolnienie pamięci, gdy naprawdę tego potrzebujesz.
źródło