Guide de WeakHashMap en Java

Guide de WeakHashMap en Java

1. Vue d'ensemble

Dans cet article, nous allons examiner unWeakHashMap du packagejava.util.

Afin de comprendre la structure des données, nous allons l'utiliser ici pour déployer une implémentation de cache simple. Cependant, gardez à l'esprit que cela a pour but de comprendre le fonctionnement de la carte, et la création de votre propre implémentation de cache est presque toujours une mauvaise idée.

En termes simples,WeakHashMap est une implémentation basée sur une table de hachage de l'interfaceMap, avec des clés de typeWeakReference.

Une entrée dans unWeakHashMap sera automatiquement supprimée lorsque sa clé n'est plus utilisée normalement, ce qui signifie qu'il n'y a pas un seulReference qui pointe vers cette clé. Lorsque le processus de garbage collection (GC) rejette une clé, son entrée est effectivement supprimée de la carte, donc cette classe se comporte quelque peu différemment des autres implémentations deMap.

2. Références fortes, souples et faibles

Pour comprendre le fonctionnement deWeakHashMap,we need to look at a WeakReference class - qui est la construction de base des clés dans l'implémentation deWeakHashMap. En Java, nous avons trois principaux types de références, que nous expliquerons dans les sections suivantes.

2.1. Références solides

La référence forte est le type deReference le plus courant que nous utilisons dans notre programmation quotidienne:

Integer prime = 1;

La variableprime a unstrong reference vers un objetInteger avec la valeur 1. Tout objet ayant une référence forte pointant vers lui n'est pas éligible pour GC.

2.2. Références souples

En termes simples, un objet sur lequel unSoftReference pointe vers lui ne sera pas récupéré tant que la JVM n’aura pas absolument besoin de mémoire.

Voyons comment nous pouvons créer unSoftReference en Java:

Integer prime = 1;
SoftReference soft = new SoftReference(prime);
prime = null;

L'objetprime a une référence forte pointant vers lui.

Ensuite, nous enveloppons la référence forte deprimedans une référence souple. Après avoir fait cette forte référencenull, un objetprime est éligible pour GC mais ne sera collecté que lorsque JVM a absolument besoin de mémoire.

2.3. Références faibles

Les objets qui sont référencés uniquement par des références faibles sont récupérés avec empressement; le GC n’attendra pas qu’il ait besoin de mémoire dans ce cas.

Nous pouvons créer unWeakReference en Java de la manière suivante:

Integer prime = 1;
WeakReference soft = new WeakReference(prime);
prime = null;

Lorsque nous avons fait une référenceprimenull, l'objetprime sera récupéré lors du prochain cycle GC, car il n'y a pas d'autre référence forte pointant vers lui.

Les références de typeWeakReference sont utilisées comme clés dansWeakHashMap.

3. WeakHashMap en tant que cache mémoire efficace

Supposons que nous souhaitons créer un cache qui conserve les grands objets d'image en tant que valeurs et les noms d'image en tant que clés. Nous voulons choisir une implémentation de carte appropriée pour résoudre ce problème.

Utiliser un simpleHashMap ne sera pas un bon choix car les objets de valeur peuvent occuper beaucoup de mémoire. De plus, ils ne seront jamais récupérés du cache par un processus GC, même s'ils ne sont plus utilisés dans notre application.

Idéalement, nous voulons une implémentation deMap qui permette à GC de supprimer automatiquement les objets inutilisés. Lorsqu'une clé d'un grand objet image n'est pas utilisée dans notre application, à un endroit quelconque, cette entrée sera supprimée de la mémoire.

Heureusement, leWeakHashMap a exactement ces caractéristiques. Testons nosWeakHashMap et voyons comment ils se comportent:

WeakHashMap map = new WeakHashMap<>();
BigImage bigImage = new BigImage("image_id");
UniqueImageName imageName = new UniqueImageName("name_of_big_image");

map.put(imageName, bigImage);
assertTrue(map.containsKey(imageName));

imageName = null;
System.gc();

await().atMost(10, TimeUnit.SECONDS).until(map::isEmpty);

Nous créons une instanceWeakHashMap qui stockera nos objetsBigImage. Nous mettons un objetBigImage comme valeur et une référence d'objetimageName comme clé. LesimageName seront stockés dans une carte en tant que typeWeakReference.

Ensuite, nous définissons la référenceimageName surnull, il n'y a donc plus de références pointant vers l'objetbigImage. Le comportement par défaut d'unWeakHashMap est de récupérer une entrée qui n'a aucune référence à elle sur le prochain GC, donc cette entrée sera supprimée de la mémoire par le prochain processus GC.

Nous appelons unSystem.gc() pour forcer la JVM à déclencher un processus GC. Après le cycle GC, nosWeakHashMap seront vides:

WeakHashMap map = new WeakHashMap<>();
BigImage bigImageFirst = new BigImage("foo");
UniqueImageName imageNameFirst = new UniqueImageName("name_of_big_image");

BigImage bigImageSecond = new BigImage("foo_2");
UniqueImageName imageNameSecond = new UniqueImageName("name_of_big_image_2");

map.put(imageNameFirst, bigImageFirst);
map.put(imageNameSecond, bigImageSecond);

assertTrue(map.containsKey(imageNameFirst));
assertTrue(map.containsKey(imageNameSecond));

imageNameFirst = null;
System.gc();

await().atMost(10, TimeUnit.SECONDS)
  .until(() -> map.size() == 1);
await().atMost(10, TimeUnit.SECONDS)
  .until(() -> map.containsKey(imageNameSecond));

Notez que seule la référenceimageNameFirst est définie surnull. La référenceimageNameSecond reste inchangée. Une fois GC déclenché, la carte ne contiendra qu'une seule entrée -imageNameSecond.

4. Conclusion

Dans cet article, nous avons examiné les types de références en Java pour bien comprendre le fonctionnement dejava.util.WeakHashMap. Nous avons créé un cache simple qui exploite le comportement d'unWeakHashMap et teste s'il fonctionne comme prévu.

L'implémentation de tous ces exemples et extraits de code se trouve dans leGitHub project - qui est un projet Maven, il devrait donc être facile à importer et à exécuter tel quel.