Quando useresti una WeakHashMap o una WeakReference?


163

L'uso di riferimenti deboli è qualcosa di cui non ho mai visto un'implementazione, quindi sto cercando di capire quale sia il caso d'uso per loro e come funzionerebbe l'implementazione. Quando hai dovuto usare un WeakHashMapoe WeakReferencecome è stato usato?



Risposte:


96

Un problema con riferimenti forti è la memorizzazione nella cache, in particolare con strutture molto grandi come le immagini. Supponiamo di avere un'applicazione che deve lavorare con immagini fornite dall'utente, come lo strumento di progettazione del sito Web su cui lavoro. Naturalmente vuoi memorizzare queste immagini nella cache, perché caricarle dal disco è molto costosa e vuoi evitare la possibilità di avere due copie dell'immagine (potenzialmente gigantesca) in memoria contemporaneamente.

Poiché una cache di immagini dovrebbe impedirci di ricaricare le immagini quando non ne abbiamo assolutamente bisogno, ti renderai presto conto che la cache dovrebbe sempre contenere un riferimento a qualsiasi immagine che è già in memoria. Con normali riferimenti forti, tuttavia, quel riferimento stesso costringerà l'immagine a rimanere in memoria, il che richiede in qualche modo determinare quando l'immagine non è più necessaria in memoria e rimuoverla dalla cache, in modo che diventi idonea per la garbage collection. Sei costretto a duplicare il comportamento del Garbage Collector e a determinare manualmente se un oggetto deve essere o meno in memoria.

Comprensione dei riferimenti deboli , Ethan Nicholas


43
SoftReferences non sarebbe migliore in questo caso, vale a dire i riferimenti che vengono raccolti solo quando la memoria inizia a esaurirsi.
JesperE,

Sono un po 'confuso ... diciamo che ho una cache di immagini SWT. Le immagini SWT devono essere DISPOSTE tramite il metodo dispose () per rilasciare risorse SO. Se uso una WeakHashMap per memorizzarli, mostra esattamente che il GC disporrà l'oggetto?
marcolopes,

2
@marcolopes il GC userebbe un finalizzatore su qualsiasi altro oggetto. Sembra che a SWT non piaccia quando lo fai, quindi non penso che puoi gestire le risorse del sistema operativo con a WeakHashMap.
Jacob Krall,

@marcolopes, (suppongo che il tuo GC garantisca la finalizzazione di una chiamata prima di recuperare la memoria.) Se lo smaltimento viene effettuato nel finalizzatore della cache, va tutto bene. Se dispose è qualcosa che devi chiamare manualmente, allora 1) estendi la classe e metti dispose nel finalizzatore, oppure 2) usa phantomreference per tracciare ed eseguire disposose di conseguenza. L'opzione 2 è migliore (evita i bug di resurrezione e ti dà la possibilità di eseguire lo smaltimento su un altro thread), ma l'opzione 1 è più facile da implementare senza helperclasses.
Pacerier,


55

WeakReference contro SoftReference

Una distinzione da chiarire è la differenza tra a WeakReferencee a SoftReference.

Fondamentalmente a WeakReferencesarà GC-d dalla JVM avidamente, una volta che l'oggetto referenziato non ha riferimenti concreti ad esso. Un SoftReferenceoggetto d, d'altra parte, tenderà a essere lasciato in giro dal garbage collector fino a quando non avrà davvero bisogno di recuperare la memoria.

Una cache in cui i valori sono contenuti all'interno di WeakReferences sarebbe piuttosto inutile (in a WeakHashMap, sono le chiavi a cui si fa debolmente riferimento). SoftReferencessono utili per racchiudere i valori quando si desidera implementare una cache che può crescere e ridursi con la memoria disponibile.


4
"Una cache in cui i valori sono contenuti in WeakReferences sarebbe piuttosto inutile" Non sono assolutamente d'accordo.
Thomas Eding,

5
@trinithis - erm, non so davvero cosa dire. Perché una cache i cui valori scompaiono nel momento in cui non si fa riferimento a loro è esattamente una cosa utile ?
oxbow_lakes,

4
Per qualcosa come la memoizzazione, una cache che contiene vagamente i suoi valori memorizzati nella cache potrebbe essere utile.
Thomas Eding,

5
@ThomasEding Ancora non capisco. L'unica volta in cui una cache sembra utile è quando non ci sono altri riferimenti ad essa ... Se hai riferimenti ad essa, per cosa hai bisogno di una cache?
Cruncher,

2
@ThomasEding, Softref dice all'ambiente "conservalo finché non hai memoria". Weakref dice all'ambiente "archiviare questo fino a quando GC funziona". Francamente non esiste un caso d'uso per deboli, a meno che non si stia eseguendo il debug / profiling del GC stesso. Se si desidera una cache sensibile alla memoria, utilizzare softref. Se non si desidera una cache, non memorizzarla nella cache! Da dove arriva il punto debole?
Pacerier,

30

Un uso comune di WeakReferences e WeakHashMapin particolare è per l'aggiunta di proprietà agli oggetti. Occasionalmente vuoi aggiungere alcune funzionalità o dati a un oggetto ma la sottoclasse e / o la composizione non sono un'opzione in quel caso la cosa ovvia da fare sarebbe creare una hashmap che collega l'oggetto che vuoi estendere alla proprietà che vuoi aggiungere . quindi ogni volta che hai bisogno della proprietà puoi semplicemente cercarla nella mappa. Tuttavia, se gli oggetti a cui stai aggiungendo proprietà tendono a essere distrutti e creati molto, puoi finire con un sacco di vecchi oggetti nella tua mappa che occupano molta memoria.

Se usi un WeakHashMapinvece gli oggetti lasceranno la tua mappa non appena non saranno più utilizzati dal resto del programma, che è il comportamento desiderato.

Ho dovuto fare questo per aggiungere alcuni dati per java.awt.Componentaggirare un cambiamento nel JRE tra la 1.4.2 e 1.5, avrei potuto riparato da tutti i componenti sottoclasse ero int interessato ( JButton, JFrame, JPanel....), ma questo è stato molto più facile con molto meno codice.


1
come fa WeakHashMap a sapere che "non sono più utilizzati dal resto del programma"?
Vinoth Kumar CM,

2
Una hasmap debole utilizza riferimenti deboli per le sue chiavi. Quando un oggetto viene referenziato solo attraverso riferimenti deboli, il garbage collector "notificherà" al proprietario il riferimento debole (in questo caso la WeaHashMap). Leggerei su WeakReferences e ReferenceQueues nei javadocs per capire come questi oggetti interagiscono con il Garbage Collector.
Luca,

1
grazie Luca, puoi fornire un semplice codice per la tua descrizione?
acqua bollita,

Pertanto, il riferimento debole ha senso solo in Java a causa della strana API di Java in cui alcune classi non possono essere estese.
Pacerier,

22

Un altro caso utile per WeakHashMaped WeakReferenceè un'implementazione del registro dell'ascoltatore .

Quando crei qualcosa che vuole ascoltare determinati eventi, di solito registri un ascoltatore, ad es

manager.registerListener(myListenerImpl);

Se managermemorizza il tuo listener con a WeakReference, ciò significa che non è necessario rimuovere il registro, ad esempio con a manager.removeListener(myListenerImpl)perché verrà automaticamente rimosso una volta che il tuo listener o il tuo componente che detiene il listener diventa non disponibile.

Ovviamente puoi ancora rimuovere manualmente il tuo ascoltatore, ma se non lo dimentichi o lo dimentichi, non causerà una perdita di memoria e non impedirà che il tuo ascoltatore venga raccolto.

Da dove WeakHashMapviene l'immagine?

Il registro degli ascoltatori che desidera archiviare gli ascoltatori registrati come WeakReferences ha bisogno di una raccolta per memorizzare questi riferimenti. Non esiste WeakHashSetun'implementazione nella libreria Java standard solo a, WeakHashMapma possiamo facilmente usare quest'ultima per "implementare" la funzionalità della prima:

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

Con questo listenerSetper registrare un nuovo listener devi solo aggiungerlo al set, e anche se non viene rimosso esplicitamente, se il listener non viene più referenziato, verrà rimosso automaticamente dalla JVM.


10
Il problema con l'uso di weakHashSets per l'elenco di listener è che le istanze di listener anonime create in register () andranno perse facilmente, il che sarebbe inaspettato per l'utente. È più sicuro tenere invece riferimenti più forti agli ascoltatori dei manager e fare affidamento sul chiamante per fare la cosa giusta.
Gunanaresh,

Per l'implementazione del registro degli ascoltatori: tutti i listener registrati verranno raccolti / distrutti al prossimo calcio GC? Ad esempio, mentre si attiva il metodo onSomethingHappened () di tutto l'ascoltatore, cosa succede se il calcio di GC viene avviato?
Blackkara,

@icza, non riesco a credere che la gente stia ancora vendendo questo mito. Questa è una risposta completamente sbagliata. Potresti anche dire che un altro caso utile WeakHashMapè quando hai bisogno di uno HashMapdegli oggetti. Quindi wow non devi fare manualmente hashmap.remove mai perché gli oggetti vengono rimossi automagicamente una volta che l'oggetto è fuori portata! Letteralmente magico! Un brutto trucco magico è un volto completo .
Pacerier,

3
@Pacerier: Ho seguito i tuoi collegamenti da altri commenti in JavaScript fin qui, e ancora non capisco perché l'implementazione del registro degli ascoltatori con WeakMap sia un mito. Ad esempio, se i client WebSocket devono essere associati ad alcuni listener tramite il servizio di registro, sembra logico archiviare gli oggetti socket come chiavi in ​​WeakMap (per impedire che si blocchino in memoria dopo la chiusura della connessione, diciamo, in caso di errore) e potessero per recuperare tutti i loro ascoltatori, se necessario. Quindi, potresti per favore affermare, cosa c'è di esattamente sbagliato in questo approccio?
Danneggiato organico il

1
@Pacerier Anche io non riesco a capire la tua obiezione. In uno scenario Pubblica-Abbonati o Bus evento, una raccolta di riferimenti deboli ha perfettamente senso per me. L'oggetto in abbonamento può uscire dall'ambito di applicazione e andare alla garbage collection senza che sia necessario annullare la sottoscrizione formale. Tale processo di annullamento dell'iscrizione può essere particolarmente complicato se un oggetto di terze parti era responsabile della sottoscrizione iniziale a sua insaputa. Una raccolta di WeakReferencesemplifica notevolmente la base di codice ed evita inutili bug relativi alla mancata iscrizione. Quale aspetto negativo?
Basil Bourque,

5

Questo post sul blog dimostra l'uso di entrambe le classi: Java: sincronizzazione su un ID . L'utilizzo è simile al seguente:

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 fornisce oggetti basati su ID per la sincronizzazione. I requisiti sono:

  • deve restituire un riferimento allo stesso oggetto per l'uso simultaneo di ID equivalenti
  • deve restituire un oggetto diverso per ID diversi
  • nessun meccanismo di rilascio (gli oggetti non vengono restituiti al provider)
  • non deve perdere (gli oggetti inutilizzati sono idonei per la garbage collection)

Ciò si ottiene utilizzando una mappa di archiviazione interna di tipo:

WeakHashMap<Mutex, WeakReference<Mutex>>

L'oggetto è sia chiave che valore. Quando nulla di esterno alla mappa ha un forte riferimento all'oggetto, può essere spazzato via. I valori nella mappa sono memorizzati con riferimenti concreti , quindi il valore deve essere racchiuso in un riferimento debole per evitare una perdita di memoria. Quest'ultimo punto è trattato nel javadoc .


3

Ad esempio, se si desidera tenere traccia di tutti gli oggetti creati di una determinata classe. Per consentire comunque la raccolta differenziata di questi oggetti, è necessario mantenere un elenco / una mappa di riferimenti deboli agli oggetti anziché agli oggetti stessi.

Ora, se qualcuno potesse spiegarmi riferimenti fantasma, sarei felice ...


2
Un uso: PhantomReferences consente di determinare esattamente quando un oggetto è stato rimosso dalla memoria. Sono infatti l'unico modo per determinarlo. ( weblogs.java.net/blog/enicholas/archive/2006/05/… )
Jacob Krall

In realtà non viene rimosso fino a quando non lo si cancella esplicitamente. "A differenza dei riferimenti deboli e deboli, i riferimenti fantasma non vengono cancellati automaticamente dal Garbage Collector mentre vengono accodati. Un oggetto che è raggiungibile tramite riferimenti fantasma rimarrà tale fino a quando tutti questi riferimenti non saranno cancellati o diventeranno essi stessi irraggiungibili."
incontro

@jontro, ma è già stato finalizzato , tutti i membri se ne sono andati. In effetti, è un oggetto vuoto. Vedere stackoverflow.com/q/7048767/632951
Pacerier

3

Come indicato sopra, i riferimenti deboli vengono mantenuti fino a quando esiste un riferimento forte.

Un esempio potrebbe essere l'uso di WeakReference all'interno dei listener, in modo che i listener non siano più attivi una volta sparito il riferimento principale al loro oggetto target. Si noti che ciò non significa che WeakReference venga rimosso dall'elenco dei listener, è comunque necessario ripulire ma può essere eseguito, ad esempio, in orari programmati. Questo ha anche l'effetto di impedire all'oggetto ascoltato di contenere riferimenti forti e alla fine essere una fonte di memoria gonfia. Esempio: componenti della GUI di Swing che fanno riferimento a un modello con un ciclo di vita più lungo rispetto alla finestra.

Giocando con gli ascoltatori come descritto sopra, ci siamo rapidamente resi conto che gli oggetti venivano raccolti "immediatamente" dal punto di vista dell'utente.


Grazie risposta utile. Ma mi chiedo, in tal caso, gli ascoltatori dovrebbero essere registrati (referenziati) con forza?
Blackkara,

Questa risposta è completamente sbagliata. Elaborazione: stackoverflow.com/questions/154724/...
Pacerier

@Pacerier - per il WeakReferencestuo commento è completamente sbagliato!

2

Un uso nel mondo reale che ho avuto per WeakReferences è se hai un singolo oggetto molto grande che viene usato raramente. Non vuoi tenerlo in memoria quando non è necessario; ma, se un altro thread ha bisogno dello stesso oggetto, non ne vuoi nemmeno due in memoria. È possibile mantenere un riferimento debole all'oggetto da qualche parte e riferimenti duri nei metodi che lo utilizzano; quando entrambi i metodi finiscono, l'oggetto verrà raccolto.


1
Questo è un riferimento debole, non un riferimento debole. Vedere stackoverflow.com/a/155492/632951
Pacerier


-1

è possibile utilizzare weakhashmap per implementare una memorizzazione nella cache senza risorse per la creazione di oggetti espansiva.

ma nota che non è desiderabile avere oggetti mutabili. l'ho usato per memorizzare nella cache i risultati delle query (che richiedono circa 400 ms per essere eseguiti) su un motore di ricerca testuale, che raramente viene aggiornato.


Stai parlando di un riferimento debole, non di un riferimento debole. Vedere stackoverflow.com/a/155492/632951
Pacerier
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.