Quand utiliseriez-vous un WeakHashMap ou un WeakReference?


163

L'utilisation de références faibles est quelque chose dont je n'ai jamais vu d'implémentation, donc j'essaie de comprendre quel est leur cas d'utilisation et comment l'implémentation fonctionnerait. Quand avez-vous eu besoin d'utiliser un WeakHashMapou WeakReferenceet comment l'a-t-il été?



Réponses:


96

Un problème avec les références fortes est la mise en cache, en particulier avec les très grandes structures comme les images. Supposons que vous ayez une application qui doit fonctionner avec des images fournies par l'utilisateur, comme l'outil de conception de site Web sur lequel je travaille. Naturellement, vous voulez mettre ces images en cache, car leur chargement à partir du disque coûte très cher et vous voulez éviter la possibilité d'avoir deux copies de l'image (potentiellement gigantesque) en mémoire à la fois.

Parce qu'un cache d'images est censé nous empêcher de recharger des images lorsque nous n'en avons pas absolument besoin, vous vous rendrez vite compte que le cache doit toujours contenir une référence à toute image déjà en mémoire. Avec des références fortes ordinaires, cependant, cette référence elle-même forcera l'image à rester en mémoire, ce qui vous obligera à déterminer quand l'image n'est plus nécessaire en mémoire et à la supprimer du cache, afin qu'elle devienne éligible pour le garbage collection. Vous êtes obligé de dupliquer le comportement du garbage collector et de déterminer manuellement si un objet doit ou non être en mémoire.

Comprendre les références faibles , Ethan Nicholas


43
Les SoftReferences ne seraient-ils pas mieux dans ce cas, c'est-à-dire des références qui ne sont collectées que lorsque la mémoire commence à manquer.
JesperE

Je suis un peu confus ... disons que j'ai un cache d'image SWT. Les images SWT doivent être ÉLIMINÉES via la méthode dispose () pour libérer les ressources SO. Si j'utilise un WeakHashMap pour les stocker, montrer exactement le GC supprimera-t-il l'objet?
marcolopes

2
@marcolopes le GC utiliserait un finaliseur sur n'importe quel autre objet. Il semble que SWT n'aime pas ça quand vous faites ça, donc je ne pense pas que vous puissiez gérer les ressources du système d'exploitation avec un WeakHashMap.
Jacob Krall

@marcolopes, (je suppose que votre GC garantit un appel à finaliser avant de récupérer de la mémoire.) Si l'élimination est effectuée dans le finaliseur de cache, tout va bien. Si disposer est quelque chose que vous devez appeler manuellement, alors soit 1) étendez la classe et placez dispose dans le finaliseur, soit 2) utilisez phantomreference pour suivre et exécuter disposer en conséquence. L'option 2 est meilleure (évite les bogues de résurrection et vous donne la possibilité d'exécuter Dispose sur un autre thread), mais l'option 1 est plus facile à implémenter sans helperclasses.
Pacerier


55

WeakReference contre SoftReference

Une distinction sur laquelle il faut être clair est la différence entre a WeakReferenceet a SoftReference.

Fondamentalement, un WeakReferencesera GC-d par la JVM avec impatience, une fois que l'objet référencé n'y aura pas de références matérielles . Un SoftReferenceobjet d, d'autre part, aura tendance à être laissé par le ramasse-miettes jusqu'à ce qu'il ait vraiment besoin de récupérer la mémoire.

Un cache où les valeurs sont conservées dans WeakReferences serait assez inutile (dans a WeakHashMap, ce sont les clés qui sont faiblement référencées). SoftReferencessont utiles pour envelopper les valeurs lorsque vous souhaitez implémenter un cache qui peut augmenter et diminuer avec la mémoire disponible.


4
"Un cache où les valeurs sont conservées dans WeakReferences serait assez inutile" Je ne suis pas du tout d'accord.
Thomas Eding

5
@trinithis - euh, je ne sais pas vraiment quoi dire. Pourquoi un cache dont les valeurs disparaissent au moment où vous ne les référencez pas est une chose utile , exactement?
oxbow_lakes

4
Pour quelque chose comme la mémorisation, un cache qui contient vaguement ses valeurs mises en cache pourrait être utile.
Thomas Eding

5
@ThomasEding Je ne comprends toujours pas. Le seul moment où un cache semble utile, c'est lorsqu'il n'y a pas d'autres références à celui-ci ... Si vous en avez des références, pourquoi avez-vous besoin d'un cache?
Cruncher

2
@ThomasEding, Softref dit à l'environnement "stockez ceci jusqu'à ce que vous n'ayez plus de mémoire". Weakref dit à l'environnement "stocker ceci jusqu'à ce que GC fonctionne". Il n'y a franchement aucun cas d'utilisation pour lowref, sauf si vous déboguez / profilez le GC lui-même. Si vous voulez un cache sensible à la mémoire, utilisez softref. Si vous ne voulez pas de cache, ne le cachez pas! D'où vient le faibleref?
Pacerier

30

Une utilisation courante de WeakReferences et WeakHashMaps en particulier consiste à ajouter des propriétés aux objets. Parfois, vous souhaitez ajouter des fonctionnalités ou des données à un objet, mais le sous-classement et / ou la composition ne sont pas une option.Dans ce cas, la chose évidente à faire serait de créer un hashmap reliant l'objet que vous souhaitez étendre à la propriété que vous souhaitez ajouter . puis chaque fois que vous avez besoin de la propriété, vous pouvez simplement la rechercher sur la carte. Cependant, si les objets auxquels vous ajoutez des propriétés ont tendance à être détruits et créés beaucoup, vous pouvez vous retrouver avec un grand nombre d'objets anciens dans votre carte occupant beaucoup de mémoire.

Si vous utilisez à la WeakHashMapplace, les objets quitteront votre carte dès qu'ils ne seront plus utilisés par le reste de votre programme, ce qui est le comportement souhaité.

Je devais le faire pour ajouter quelques données java.awt.Componentpour contourner un changement dans l'entre JRE et 1,5 1.4.2, je aurais pu la réparer par le sous - classement tous les composants que j'étais intéressé int ( JButton, JFrame, JPanel....) mais cela était beaucoup plus facile avec beaucoup moins de code.


1
comment WeakHashMap sait-il "qu'ils ne sont plus utilisés par le reste de votre programme"?
Vinoth Kumar CM le

2
Un hasmap faible utilise des références faibles pour ses clés. Lorsqu'un objet n'est référencé que par des références faibles, le garbage collector «notifiera» le propriétaire de la référence faible (dans ce cas, le WeaHashMap). Je lirais sur WeakReferences et ReferenceQueues dans les javadocs pour comprendre comment ces objets interagissent avec le garbage collector.
luke le

1
merci luke, pouvez-vous fournir un code simple pour la description ci-dessus?
eau bouillie

Ainsi, une référence faible n'a de sens qu'en Java en raison de l'API excentrique de Java où certaines classes ne peuvent pas être étendues.
Pacerier

22

Un autre cas utile pour WeakHashMapet WeakReferenceest une implémentation de registre d'écoute .

Lorsque vous créez quelque chose qui veut écouter certains événements, vous enregistrez généralement un auditeur, par exemple

manager.registerListener(myListenerImpl);

Si le managerstocke votre auditeur avec un WeakReference, cela signifie que vous n'avez pas besoin de supprimer le registre, par exemple avec un manager.removeListener(myListenerImpl)car il sera automatiquement supprimé une fois que votre auditeur ou votre composant contenant l'écouteur devient indisponible.

Bien sûr, vous pouvez toujours supprimer manuellement votre auditeur, mais si vous ne le faites pas ou si vous l'oubliez, cela ne provoquera pas de fuite de mémoire et n'empêchera pas votre auditeur d'être récupéré.

D'où WeakHashMapvient la photo?

Le registre d'écouteurs qui souhaite stocker les écouteurs enregistrés en tant que WeakReferences a besoin d'une collection pour stocker ces références. Il n'y a pas d' WeakHashSetimplémentation dans la bibliothèque standard Java seulement WeakHashMapmais nous pouvons facilement utiliser cette dernière pour "implémenter" la fonctionnalité de la première:

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

Avec cela listenerSetpour enregistrer un nouvel écouteur il suffit de l'ajouter à l'ensemble, et même s'il n'est pas supprimé explicitement, si l'écouteur n'est plus référencé, il sera automatiquement supprimé par la JVM.


10
Le problème avec l'utilisation de lowHashSets pour la liste d'écouteurs est que les instances d'écouteurs anonymes créées dans register () seront facilement perdues, ce qui serait inattendu par l'utilisateur. Il est plus sûr d'avoir des références plus solides aux auditeurs de la part des gestionnaires et de compter sur l'appelant pour faire ce qu'il faut à la place.
Gunanaresh

Pour l'implémentation du registre des auditeurs: tous les auditeurs enregistrés seront collectés / détruits lors du prochain coup d'envoi du GC? Par exemple, lors du déclenchement de la méthode onSomethingHappened () de tous les écouteurs, que se passe-t-il si GC démarre?
blackkara

@icza, je ne peux pas croire que les gens colportent encore ce mythe. C'est une réponse complètement fausse. Vous pourriez aussi bien dire qu'un autre cas utile WeakHashMapest chaque fois que vous avez besoin d'un HashMapobjet. Donc , wow vous ne devez pas faire manuellement hashmap.remove jamais parce que les articles sont automagiquement enlevés une fois que l'obj est hors de portée! Littéralement magique! Un hack magique aussi laid est un facepalm complet .
Pacerier

3
@Pacerier: J'ai suivi vos liens à partir d'autres commentaires en JavaScript, et je ne comprends toujours pas pourquoi l'implémentation du registre d'auditeur avec WeakMap est un mythe. Par exemple, si les clients WebSocket doivent être liés à certains écouteurs via le service de registre, il semble logique de stocker les objets de socket en tant que clés dans WeakMap (pour les empêcher de se bloquer en mémoire après la fermeture de la connexion, par exemple, en cas d'erreur) et de pouvoir pour récupérer tous leurs auditeurs si besoin. Alors, pourriez-vous s'il vous plaît dire, quel est exactement le problème avec cette approche?
Endommagé organique du

1
@Pacerier Je ne comprends pas non plus votre objection. Dans un scénario de publication-abonnement ou de bus d'événements, une collection de références faibles me semble parfaitement logique. L'objet d'abonnement est autorisé à sortir de la portée et à se diriger vers le ramasse-miettes sans qu'il soit nécessaire de se désabonner formellement. Ce processus de désabonnement peut être particulièrement compliqué si un objet tiers était responsable de l'abonnement initial à l'insu de l'objet d'abonnement. Une collection de WeakReferencesimplifie considérablement la base de code et évite les bogues inutiles liés à l'échec de la désinscription. Quel inconvénient?
Basil Bourque

5

Ce billet de blog montre l'utilisation des deux classes: Java: synchronisation sur un ID . L'utilisation va quelque chose comme ceci:

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 fournit des objets basés sur l'ID sur lesquels se synchroniser. Les exigences sont:

  • doit renvoyer une référence au même objet pour une utilisation simultanée d'ID équivalents
  • doit renvoyer un objet différent pour différents ID
  • pas de mécanisme de libération (les objets ne sont pas retournés au fournisseur)
  • ne doit pas fuir (les objets inutilisés sont éligibles pour le garbage collection)

Ceci est réalisé en utilisant une carte de stockage interne de type:

WeakHashMap<Mutex, WeakReference<Mutex>>

L'objet est à la fois clé et valeur. Lorsque rien d'extérieur à la carte n'a de référence ferme à l'objet, il peut être récupéré. Les valeurs de la carte sont stockées avec des références matérielles, de sorte que la valeur doit être encapsulée dans un WeakReference pour éviter une fuite de mémoire. Ce dernier point est traité dans le javadoc .


3

Si vous voulez par exemple garder une trace de tous les objets créés d'une certaine classe. Pour continuer à permettre à ces objets d'être récupérés, vous conservez une liste / une carte des références faibles aux objets au lieu des objets eux-mêmes.

Maintenant, si quelqu'un pouvait m'expliquer des références fantômes, je serais heureux ...


2
Une utilisation: PhantomReferences vous permet de déterminer exactement quand un objet a été supprimé de la mémoire. C'est en fait le seul moyen de le déterminer. ( weblogs.java.net/blog/enicholas/archive/2006/05/… )
Jacob Krall

En fait, il n'est pas supprimé tant que vous ne le supprimez pas explicitement. "Contrairement aux références souples et faibles, les références fantômes ne sont pas automatiquement effacées par le ramasse-miettes lorsqu'elles sont mises en file d'attente. Un objet accessible via des références fantômes le restera jusqu'à ce que toutes ces références soient effacées ou deviennent elles-mêmes inaccessibles."
jontro

@jontro, Mais c'est déjà finalisé , tous les membres sont partis. En fait, c'est un obj vide. Voir stackoverflow.com/q/7048767/632951
Pacerier

3

Comme indiqué ci-dessus, les références faibles sont conservées aussi longtemps qu'une référence forte existe.

Un exemple d'utilisation serait d'utiliser WeakReference dans les écouteurs, afin que les écouteurs ne soient plus actifs une fois que la référence principale à leur objet cible a disparu. Notez que cela ne signifie pas que WeakReference est supprimé de la liste des écouteurs, le nettoyage est toujours nécessaire mais peut être effectué, par exemple, à des heures planifiées. Cela a également pour effet d'empêcher l'objet écouté de contenir des références fortes et éventuellement d'être une source de ballonnement mémoire. Exemple: composants d'interface graphique Swing faisant référence à un modèle ayant un cycle de vie plus long que la fenêtre.

En jouant avec les auditeurs comme décrit ci-dessus, nous avons rapidement réalisé que les objets sont collectés «immédiatement» du point de vue de l'utilisateur.


Merci réponse utile. Mais je me demande, dans ce cas, les auditeurs devraient-ils être enregistrés (référencés) fortement?
blackkara le

Cette réponse est complètement fausse. Élaboration: stackoverflow.com/questions/154724/…
Pacerier

@Pacerier - car WeakReferencesvotre commentaire est complètement faux!

2

Une utilisation dans le monde réel que j'ai eue de WeakReferences est si vous avez un seul objet très grand qui est rarement utilisé. Vous ne voulez pas le garder en mémoire lorsqu'il n'est pas nécessaire; mais, si un autre thread a besoin du même objet, vous n'en voulez pas non plus deux en mémoire. Vous pouvez conserver quelque part une référence faible à l'objet et des références matérielles dans les méthodes qui l'utilisent; lorsque les deux méthodes se terminent, l'objet sera collecté.


1
C'est une référence douce, pas une référence faible. Voir stackoverflow.com/a/155492/632951
Pacerier


-1

vous pouvez utiliser lowhashmap pour implémenter une mise en cache sans ressources pour une création d'objets étendue.

mais notez qu'il n'est pas souhaitable d'avoir des objets mutables. Je l'ai utilisé pour mettre en cache les résultats de la requête (qui prennent environ 400 ms à exécuter) sur un moteur de recherche de texte, qui est rarement mis à jour.


Vous parlez d'une référence douce, pas d'une référence faible. Voir stackoverflow.com/a/155492/632951
Pacerier
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.