WTF : java SoftReference

Il est parti !!! Youhou ! De qui parle-t-il me direz vous ? De l’auteur de pleins de bêtises dans l’application (pour rester poli). Il n’est resté que quelques semaines et c’est heureusement car il a fait de gros dégâts.

La crasse d’aujourd’hui est une problématique de mise en cache d’objets. Évidement, comme dans beaucoup d’applications, on a préféré réinventer la roue plutôt que d’utiliser un framework de cache tout fait. Le cache a grossi et il a fallu mettre un place un timeout pour libérer les objets inutilisés.

Notre bel (ex)architecte, a eu la bonne idée de mettre des SoftReference pour que :

Utilisation d’une référence douce qui serait facilement supprimée par le Garbage Collector en cas de besoin de mémoire.

Jusque là, je dis super, les SoftReference sont très bien pour la mise en cache d’objets susceptibles d’être réutilisés. Mais voyons un peu plus en détail :

Déclaration de la map
  1.     private static Map<KeyMapRepository, List<? extends IBaseVO>> mapRepositoryVO =
  2.             new HashMap<KeyMapRepository, List<? extends IBaseVO>>();

Tiens, étrange, la déclaration de la Map ne fait pas apparaitre les SoftReference…

Ajout d'element dans le cache
  1.         /* Utilisation d'une référence douce qui serait facilement
  2.         supprimée par le Garbage Collector en cas de besoin de mémoire .*/
  3.         SoftReference<List<E>> softResultList = null;
  4.  
  5.         List<E> mapValue = (ValueMapRepository<E>) mapRepositoryVO.get(keyRepository);
  6.         // on va chercher dans la map
  7.         if (mapValue != null) {
  8.              // l'element existe deja dans le cache
  9.              return mapValue;
  10.         } else {
  11.              // pas encore mis en cache, on va chercher la donnée
  12.              List<E> results = new ArrayList<E>();
  13.              …
  14.  
  15.              mapValue = Collections.unmodifiableList(results);
  16.  
  17.              softResultList = new SoftReference<List<E>>(mapValue);
  18.  
  19.              /* Ajout du resultat dans la map pour une prochaine utilisation
  20.             (on alimente le cache par les SoftReferences
  21.             * qui sont facilement éligibles pour la garbage
  22.             collection en cas de problème de mémoitre) */
  23.             mapRepositoryVO.put(keyRepository, softResultList.get());
  24.  
  25.             return mapValue;
  26.         }

Vous aurez surement remarquer la belle ligne mapRepositoryVO.put(keyRepository, softResultList.get()); qui met dans le cache une strong référence vers notre objet et non la softréférence qu’il a construit ?

Autre subtilité, il a mis en place un champ pour pourvoir nettoyer son cache si les objets n’ont pas été utilisés depuis longtemps. Mais hélas, il a mis l’information sur les éléments contenus dans la liste elle même !

De plus, les listes stockées dans le cache sont des unmodifiable list (Ceci pour que les utilisateurs de notre cache ne puissent faire des add ou des remove). Je vous laisse apprecier le commentaire et la fonction :

  1.     /**
  2.      * Néttoyage du cache via la politique d'éviction pour éviter l'accumulation pouvant entrainer
  3.      * des memory leak.(Méthode à revoir car les listes sont non modifiables)
  4.      */
  5.     private void cleanCache() {
  6.         int tailleMap = mapRepositoryVO.size();
  7.         if (tailleMap > 0) {
  8.             List resultList = null;
  9.  
  10.             for (final Iterator iter = mapRepositoryVO.values().iterator(); iter.hasNext();) {
  11.                 resultList = (List) iter.next();
  12.                 if (resultList != null && resultList.size() > 0) {
  13.                     for (final Iterator iterList = resultList.iterator(); iterList.hasNext();) {
  14.                         final IBaseVO oldIBaseVO = (IBaseVO) iterList.next();
  15.                         if (oldIBaseVO != null) {
  16.                             if (oldIBaseVO.isExpired(timeout)) {
  17.                                 log.debug("–Taille de la liste avant éviction du cache — : "
  18.                                         + resultList.size());
  19.                                 iterList.remove();
  20.                                 log.debug("–Taille de la liste après éviction du cache — : "
  21.                                         + resultList.size());
  22.                             }
  23.                         }
  24.                     }
  25.                 }
  26.             }
  27.         }
  28.     }

Le commentaires sont fermés.