Kiedy użyłbyś WeakHashMap lub WeakReference?

163

Użycie słabych referencji jest czymś, czego nigdy nie widziałem implementacji, więc próbuję dowiedzieć się, jaki jest ich przypadek użycia i jak ta implementacja będzie działać. Kiedy musiałeś użyć WeakHashMaplub WeakReferencei jak był używany?

18 Królik
źródło

Odpowiedzi:

96

Jednym z problemów z silnymi odwołaniami jest buforowanie, szczególnie w przypadku bardzo dużych struktur, takich jak obrazy. Załóżmy, że masz aplikację, która musi pracować z obrazami dostarczonymi przez użytkowników, na przykład narzędzie do projektowania witryn internetowych, nad którym pracuję. Oczywiście chcesz buforować te obrazy, ponieważ ładowanie ich z dysku jest bardzo kosztowne i chcesz uniknąć możliwości posiadania dwóch kopii (potencjalnie gigantycznego) obrazu w pamięci jednocześnie.

Ponieważ pamięć podręczna obrazów ma uniemożliwić nam ponowne ładowanie obrazów, gdy nie jest to absolutnie konieczne, szybko zorientujesz się, że pamięć podręczna zawsze powinna zawierać odniesienie do dowolnego obrazu, który jest już w pamięci. Jednak w przypadku zwykłych silnych odniesień, to samo odniesienie wymusi pozostanie obrazu w pamięci, co wymaga od ciebie jakoś określenia, kiedy obraz nie jest już potrzebny w pamięci i usunięcia go z pamięci podręcznej, aby kwalifikował się do czyszczenia pamięci. Jesteś zmuszony do skopiowania zachowania modułu odśmiecania pamięci i ręcznego określenia, czy obiekt powinien znajdować się w pamięci.

Zrozumienie słabych odniesień , Ethan Nicholas

Jacob Krall
źródło
43
Czy SoftReferences nie byłoby lepsze w tym przypadku, tj. Odwołania, które są zbierane tylko wtedy, gdy zaczyna brakować pamięci.
JesperE,
Jestem trochę zdezorientowany ... powiedzmy, że mam pamięć podręczną obrazów SWT. Obrazy SWT muszą zostać usunięte za pomocą metody dispose (), aby zwolnić zasoby SO. Jeśli używam WeakHashMap do ich przechowywania, pokaż dokładnie, czy GC usunie obiekt?
marcolopes
2
@marcolopes, GC użyłby finalizatora na każdym innym obiekcie. Wygląda na to, że SWT nie lubi tego, kiedy to robisz, więc nie sądzę, abyś mógł zarządzać zasobami systemu operacyjnego za pomocą pliku WeakHashMap.
Jacob Krall,
@marcolopes, (zakładam, że twój GC gwarantuje wywołanie sfinalizowania, zanim odzyska pamięć). Jeśli dispose jest zrobione w finalizatorze cache, wszystko jest w porządku. Jeśli dispose jest czymś, co musisz wywołać ręcznie, to albo 1) rozszerz klasę i umieść dispose w finalizatorze, albo 2) użyj phantomreference, aby śledzić i odpowiednio uruchomić dispose. Opcja 2 jest lepsza (pozwala uniknąć błędów wskrzeszania i daje możliwość uruchomienia dyspozycji w innym wątku), ale opcja 1 jest łatwiejsza do wdrożenia bez klas pomocniczych.
Pacerier
55

WeakReference przeciw SoftReference

Jedno rozróżnienie, które należy wyjaśnić, to różnica między a WeakReferencei a SoftReference.

Zasadniczo WeakReferencebędzie to GC-d przez JVM chętnie, gdy obiekt, do którego istnieje odwołanie, nie ma do niego żadnych twardych odniesień. Z SoftReferencedrugiej strony obiekt d będzie miał tendencję do pozostawiania go przez moduł odśmiecania, dopóki naprawdę nie będzie musiał odzyskać pamięci.

Pamięć podręczna, w której wartości są przechowywane wewnątrz WeakReferences, byłaby całkiem bezużyteczna (w a WeakHashMap, to klucze są słabo przywoływane). SoftReferencessą przydatne do zawijania wartości, gdy chcesz zaimplementować pamięć podręczną, która może rosnąć i zmniejszać się wraz z dostępną pamięcią.

oxbow_lakes
źródło
4
„Pamięć podręczna, w której wartości są przechowywane w WeakReferences, byłaby całkiem bezużyteczna”. Całkowicie się z tym nie zgadzam.
Thomas Eding,
5
@trinithis - erm, naprawdę nie wiem, co powiedzieć. Dlaczego pamięć podręczna, której wartości znikają w momencie, gdy nie odwołujesz się do nich, jest pożyteczną rzeczą ?
oxbow_lakes
4
W przypadku czegoś takiego jak zapamiętywanie przydatna może być pamięć podręczna, która luźno przechowuje wartości w pamięci podręcznej.
Thomas Eding
5
@ThomasEding Nadal nie rozumiem. Pamięć podręczna wydaje się przydatna tylko wtedy, gdy nie ma do niej innych odniesień ... Jeśli masz do niej odwołania, do czego potrzebujesz pamięci podręcznej?
Cruncher
2
@ThomasEding, Softref mówi środowisku „przechowuj to, dopóki nie będziesz mieć pamięci”. Weakref mówi środowisku „przechowuj to do uruchomienia GC”. Szczerze mówiąc, nie ma żadnego przypadku użycia slabref, chyba że debugujesz / profilujesz samą GC. Jeśli chcesz mieć pamięć podręczną wrażliwą na pamięć, użyj softref. Jeśli nie chcesz pamięci podręcznej, nie przechowuj jej! Skąd bierze się slabref?
Pacerier
30

W szczególności jednym z powszechnych zastosowań WeakReferences i WeakHashMaps jest dodawanie właściwości do obiektów. Czasami chcesz dodać jakąś funkcjonalność lub dane do obiektu, ale podklasy i / lub kompozycja nie są opcją w tym przypadku oczywistą rzeczą byłoby utworzenie hashmapy łączącej obiekt, który chcesz rozszerzyć, do właściwości, którą chcesz dodać . wtedy, gdy potrzebujesz nieruchomości, możesz ją po prostu sprawdzić na mapie. Jeśli jednak obiekty, do których dodajesz właściwości, są często niszczone i często tworzone, możesz skończyć z wieloma starymi obiektami na mapie, które zajmują dużo pamięci.

Jeśli WeakHashMapzamiast tego użyjesz a, obiekty opuszczą twoją mapę, gdy tylko nie będą już używane przez resztę twojego programu, co jest zachowaniem pożądanym.

Musiałem to zrobić, aby dodać jakieś dane java.awt.Component, aby ominąć zmianę pomiędzy JRE 1.4.2 i 1.5, mógłbym naprawić go przez instacji każdy składnik byłem zainteresowany int ( JButton, JFrame, JPanel....), ale było to o wiele łatwiej z dużo mniejszą ilością kodu.

Łukasz
źródło
1
skąd WeakHashMap wie, że „nie są już używane przez resztę Twojego programu”?
Vinoth Kumar CM
2
Słaba mapa hasłowa używa słabych odniesień do swoich kluczy. Gdy do obiektu odwołuje się tylko słabe odniesienie, moduł odśmiecania „powiadomi” właściciela słabego odniesienia (w tym przypadku WeaHashMap). Chciałbym przeczytać WeakReferences i ReferenceQueues w javadocs, aby zrozumieć, jak te obiekty współdziałają z garbage collector.
Łukasz
1
dzięki luke, czy możesz podać dla ciebie prosty kod na powyższym opisie?
boiledwater
Dlatego słabe odwołanie ma sens tylko w Javie z powodu dziwacznego API Javy, w którym niektórych klas nie można rozszerzyć.
Pacerier
22

Innym przydatnym przypadkiem dla implementacji rejestru nasłuchiwaniaWeakHashMap i WeakReferencejest .

Kiedy tworzysz coś, co chce posłuchać określonych wydarzeń, zazwyczaj rejestrujesz słuchacza, np

manager.registerListener(myListenerImpl);

Jeśli managerprzechowuje twój słuchacz z a WeakReference, oznacza to, że nie musisz usuwać rejestru, np. Z a, manager.removeListener(myListenerImpl)ponieważ zostanie on automatycznie usunięty, gdy twój słuchacz lub twój komponent, który go trzyma, stanie się niedostępny.

Oczywiście nadal możesz ręcznie usunąć nasłuchiwanie, ale jeśli tego nie zrobisz lub zapomnisz, nie spowoduje to wycieku pamięci i nie zapobiegnie gromadzeniu elementów bezużytecznych.

Gdzie pojawia WeakHashMapsię ten obraz?

Rejestr odbiornika, który chce przechowywać zarejestrowane odbiorniki jako WeakReferences, potrzebuje kolekcji do przechowywania tych odniesień. W WeakHashSetstandardowej bibliotece Javy nie ma implementacji, WeakHashMapale możemy z łatwością wykorzystać tę drugą do „zaimplementowania” funkcjonalności pierwszej:

Set<ListenerType> listenerSet =
    Collections.newSetFromMap(new WeakHashMap<ListenerType, Boolean>());

Mając to listenerSetna zarejestrowanie nowego słuchacza wystarczy dodać go do zestawu, a nawet jeśli to nie jest usuwany wyraźnie, gdy słuchacz nie jest wymieniony, zostanie on automatycznie usunięty przez JVM.

icza
źródło
10
Problem z używaniem slabychHashSets do listener list polega na tym, że anonimowe instancje detektora utworzone w register () zostaną łatwo utracone, co byłoby nieoczekiwane przez użytkownika. Zamiast tego bezpieczniej jest mieć silniejsze odniesienia do słuchaczy od menedżerów i polegać na rozmówcy, który zrobi to, co trzeba.
Gunanaresh
Dla implementacji rejestru nasłuchiwania: wszyscy zarejestrowani nasłuchujący zostaną zebrani / zniszczeni przy następnym uruchomieniu GC? Na przykład, podczas uruchamiania metody onSomethingHappened () wszystkich listenera, co się stanie, jeśli wykopie GC?
blackkara
@icza, nie mogę uwierzyć, że ludzie wciąż rozpowszechniają ten mit. To jest całkowicie błędna odpowiedź. Równie dobrze możesz powiedzieć, że inny przydatny przypadek WeakHashMapdotyczy sytuacji, gdy potrzebujesz HashMapobiektów. Więc wow, nie musisz ręcznie wykonywać hashmap.remove , ponieważ elementy są automatycznie usuwane, gdy obiekt znajdzie się poza zasięgiem! Dosłownie magia! Taka brzydka magiczna sztuczka to kompletny facepalm .
Pacerier,
3
@Pacerier: Śledziłem twoje linki z innych komentarzy w JavaScript aż do tego miejsca i nadal nie do końca rozumiem, dlaczego implementacja rejestru nasłuchiwania w WeakMap jest mitem. Na przykład, jeśli klienci WebSocket powinni być powiązani z niektórymi odbiornikami na nich za pośrednictwem usługi rejestru, logiczne wydaje się przechowywanie obiektów gniazda jako kluczy w WeakMap (aby zapobiec zawieszaniu się ich w pamięci po zamknięciu połączenia, powiedzmy, w przypadku błędu) i być w stanie aby w razie potrzeby odzyskać wszystkich swoich słuchaczy. Czy mógłbyś więc powiedzieć, co dokładnie jest złego w tym podejściu?
Uszkodzony organiczny
1
@Pacerier Ja też nie rozumiem twojego sprzeciwu. W scenariuszu publikowania i subskrybowania lub usługi Event Bus zbiór słabych odwołań ma dla mnie sens. Obiekt subskrybujący może wyjść poza zakres i przejść do czyszczenia pamięci bez konieczności formalnej rezygnacji z subskrypcji. Ten proces anulowania subskrypcji może być szczególnie skomplikowany, jeśli obiekt strony trzeciej był odpowiedzialny za początkową subskrypcję bez wiedzy obiektu subskrybującego. Zbiór WeakReferenceplików znacznie upraszcza podstawę kodu i pozwala uniknąć niepotrzebnych błędów związanych z niepowodzeniem anulowania subskrypcji. Jaki minus?
Basil Bourque
5

Ten wpis na blogu demonstruje użycie obu klas: Java: synchronizacja na podstawie identyfikatora . Użycie wygląda mniej więcej tak:

private static IdMutexProvider MUTEX_PROVIDER = new IdMutexProvider();

public void performTask(String resourceId) {
    IdMutexProvider.Mutex mutext = MUTEX_PROVIDER.getMutex(resourceId);
    synchronized (mutext) {
        // look up the resource and do something with it
    }
}

IdMutextProvider zapewnia obiekty oparte na identyfikatorach do synchronizacji. Wymagania są następujące:

  • musi zwrócić odwołanie do tego samego obiektu w celu jednoczesnego używania równoważnych identyfikatorów
  • musi zwrócić inny obiekt dla różnych identyfikatorów
  • brak mechanizmu zwalniania (obiekty nie są zwracane do dostawcy)
  • nie może przeciekać (nieużywane obiekty kwalifikują się do czyszczenia pamięci)

Osiąga się to za pomocą wewnętrznej mapy pamięci typu:

WeakHashMap<Mutex, WeakReference<Mutex>>

Obiekt jest jednocześnie kluczem i wartością. Gdy nic poza mapą nie ma twardego odniesienia do obiektu, może zostać wyrzucone do pamięci. Wartości w mapie są przechowywane z twardymi odwołaniami, więc wartość musi być opakowana w WeakReference, aby zapobiec wyciekowi pamięci. Ten ostatni punkt omówiono w pliku javadoc .

McDowell
źródło
3

Jeśli na przykład chcesz śledzić wszystkie obiekty utworzone w określonej klasie. Aby nadal zezwalać na zbieranie elementów bezużytecznych, zachowujesz listę / mapę słabych odniesień do obiektów zamiast samych obiektów.

Gdyby ktoś mógł mi wyjaśnić odniesienia do widm, byłbym szczęśliwy ...

JesperE
źródło
2
Jedno zastosowanie: PhantomReferences pozwala dokładnie określić, kiedy obiekt został usunięty z pamięci. W rzeczywistości są jedynym sposobem, aby to ustalić. ( weblogs.java.net/blog/enicholas/archive/2006/05/… )
Jacob Krall
W rzeczywistości nie jest usuwany, dopóki go wyraźnie nie wyczyścisz. „W przeciwieństwie do miękkich i słabych odniesień, odniesienia fantomowe nie są automatycznie usuwane przez moduł wyrzucania elementów bezużytecznych podczas kolejkowania. Obiekt, do którego można uzyskać dostęp za pośrednictwem odniesień fantomowych, pozostanie taki, dopóki wszystkie takie odniesienia nie zostaną wyczyszczone lub same staną się nieosiągalne”.
jontro
@jontro, Ale to już zostało sfinalizowane , wszyscy członkowie odeszli. W rzeczywistości jest to pusty obiekt. Zobacz stackoverflow.com/q/7048767/632951
Pacerier
3

Jak stwierdzono powyżej, słabe odniesienie utrzymuje się tak długo, jak istnieje silne odniesienie.

Przykładowym zastosowaniem może być użycie WeakReference wewnątrz detektorów, tak aby nasłuchiwacze nie byli już aktywni po zniknięciu głównego odniesienia do ich obiektu docelowego. Należy zauważyć, że nie oznacza to, że WeakReference jest usuwane z listy słuchaczy, czyszczenie jest nadal wymagane, ale można je wykonać na przykład w zaplanowanych godzinach. Ma to również wpływ na to, że słuchany obiekt nie posiada silnych odniesień i ostatecznie staje się źródłem nadmiaru pamięci. Przykład: komponenty Swing GUI odwołujące się do modelu mającego dłuższy cykl życia niż okno.

Podczas zabawy ze słuchaczami, jak opisano powyżej, szybko zdaliśmy sobie sprawę, że obiekty są zbierane „natychmiast” z punktu widzenia użytkownika.

Louis Jacomet
źródło
Dzięki przydatnej odpowiedzi. Ale zastanawiam się, czy w takim przypadku słuchacze powinny być silnie rejestrowane (do których można się odwoływać)?
blackkara
Ta odpowiedź jest całkowicie błędna. Opracowanie: stackoverflow.com/questions/154724/…
Pacerier
@Pacerier - WeakReferencesTwój komentarz jest całkowicie błędny!
2

Jednym z rzeczywistych zastosowań, jakie miałem dla WeakReferences, jest pojedynczy, bardzo duży obiekt, który jest rzadko używany. Nie chcesz mieć tego w pamięci, gdy nie jest potrzebny; ale jeśli inny wątek potrzebuje tego samego obiektu, nie chcesz też dwóch z nich w pamięci. Możesz przechowywać gdzieś słabe odniesienie do obiektu, a twarde odniesienia w metodach, które go używają; gdy obie metody zakończą się, obiekt zostanie zebrany.


źródło
1
To jest odniesienie miękkie, a nie słabe. Zobacz stackoverflow.com/a/155492/632951
Pacerier
-1

możesz użyć slabyhashmap, aby zaimplementować buforowanie bez zasobów do ekspansywnego tworzenia obiektów.

ale zauważ, że nie jest pożądane posiadanie zmiennych obiektów. Użyłem go do buforowania wyników zapytań (których wykonanie zajmuje około 400 ms) do wyszukiwarki tekstowej, która rzadko jest aktualizowana.

Andreas Petersson
źródło
Mówisz o miękkim odwołaniu, a nie o słabym odwołaniu. Zobacz stackoverflow.com/a/155492/632951
Pacerier