Java: Soft References (cómo evitar o minimizar el OutOfMemoryError)

(Miguel esto seguro que te viene bien) 🙂

Las aplicaciones que necesitan almacenar muchos datos en memoria tarde o temprano llegan al temido java.lang.OutOfMemoryError y tienen que controlar el tamaño de los datos en memoria.

Por defecto todos los objetos en la caché de referencias son Hard References (la JVM puede limpiarlas sólo si son de dereferenciados).

Para solventar este problema aparece el concepto de las Soft References que a diferencia de las Hard Reference pueden ser recogidos por el Garbage Collector.

Existen 3 tipos de Sof References (cada una más débil): soft, weak, y phantom.

Las Soft References se deben usar para implementar cachés sensibles a disponer de poca memoria (la aplicación debe comprobar la existencia del objeto en la caché antes de invocar un método sobre el mismo. Si el objeto en caché se recogió por el GC entonces la aplicación tiene para recrear el objeto y volverlo a poner en la caché.

Y ahora el ejemplo (esta vez sin pantallazo para que lo podamos copiar :))

public class SoftCacheMap implements Map{

private final Map cacheMap;

private Thread cleanupThread;

private final ReferenceQueue clearedReferences;

private static class Entry extends SoftReference {

private final Object _key;

public Entry(Object key, Object value, ReferenceQueue queue) {

super(value, queue);

_key = key;

}

final Object getKey() {

return _key;

}

final Object getValue() {

return this.get();

}

}

public Object put(Object key, Object o) {

SoftReference refKey =new SoftCacheMap.Entry(key, o,clearedReferences);

Object obj = null;

synchronized (cacheMap) {

obj = cacheMap.put(key, refKey);

}

if(cacheMap.size() > peakSize)

peakSize = cacheMap.size();

if(!referenceQueueCleanupThread)

removeClearedReferences();

return obj;

}

public Object get(Object key) {

if(!referenceQueueCleanupThread)

removeClearedReferences();

if (cacheMap.size() > 0) {

SoftReference sr = (SoftReference)cacheMap.get(key);

synchronized (cacheMap) {

cacheMap.remove(key);

}

if(sr!=null)

return sr.get();

}

return null;

}

}

Respuestas

  1. Hola, me gustaria saber su opinion si realmente utilizar SoftReferences como objetos a almacenar en una cache es una buena idea, por lo que yo se, que no es mucho, en teoria estos objetos son elegibles por el GC un poquito antes de de que llegue el temido OutOfMemory, pero usando este tipo de referencias estamos perdiendo el control de la cache pues no es mi algoritmo el que se encarga de borrar esa referencia de la cache, por lo que cada vez que tenga que ir a la cache, tendre que traerme el objeto de la bd y volverlo a poner en la cache, cuando se supone que deberia estar ahí y no provocar el fallo de cache. Si la memoria vuelve a escasear, el GC vuelve a actuar y vuelta otra vez a empezar con el consiguiente consumo de memoria y cpu que provoca la accion del GC. Como ve, hay un circulo vicioso entre lo que necesita el GC de la cpu del sistema para funcionar, por lo que yo veo usando SoftReferences voy a provocar la accion del GC mas de la cuenta.

    Le he resumido mi experiencia y me gustaria saber su opinion sobre como evitar este problema, por lo que yo veo, la unica solucion es controlar (medir y controlar finamente la JVM con herramientas que permitan ver el estado de la misma en situaciones de producción) cuantos objetos de tipo Strong puedes alojar en tu cache (un ConcurrentHashMap por ej con threads que se encargan de traer el valor dado su clave) y si no caben mas objetos, pues necesitaras otra aplicación corriendo en otro servidor de aplicaciones con los siguientes datos y así hasta lo que necesites.

    Perdon por el ladrillo y disculpeme si le estoy dando la brasa

    1. ¡Gracias por la consulta! Si tienes cualquier comentarios no dudes en escribir.

      Las referencias weak y soft hacen de sus objetos apuntados elegibles por el gc cuando no poseen ninguna otra referencia de tipo directo.
      Dicho de otro modo, un objeto será eliminado de la memoria si ningún otro objeto lo referencia; y las referencias weak y soft no cuentan para esta condición.

      En java las referencias soft y weak no son mas que objetos “contenedores” del objeto referenciado. Si colocamos uno de estos objetos contenedores en una caché, la caché se encargará de mantener el objeto, pero no tendrás –efectivamente- el control del contenido (la caché no sabe nada del contenido de sus elementos).

      El uso de contenedores weak y soft puede ser muy efectivo en caches que te haces tú mismo. Una cache “artesanal” hecha con softreferences liberará los objetos cuando falte memoria, controlarás la operación de limpieza de elementos vacios, controlarás los hilos que la mantienen.

      Pero en caso de que elijas implementar soluciones de terceros como ehchache, el uso de elementos wek y soft será redundante y contraproducente:

      Redundante porque las mismas operaciones de mantenimiento y limpieza ya las realizarán las librerías que has elegido. Por ejemplo la propiedad maxBytesLocalHeap de ehcache mantienen el control de la memoria ocupada. Ya tiene sus propios hilos muy probados para el mantenimiento.
      Contraproducente, porque el comportamiento estándar de una caché no tendrá en cuenta que sus elementos pueden “volatilizarse” debido a causas externas. Como poco perderá en eficiencia, devolviéndote objetos que considera correctos y en realidad son contenedores vacios que tienes que volver a cargar. En este caso la caché deja de ser una herramienta óptima.

      Si no tienes más remedio que guardar elementos volatiles en una caché (que puede pasar de todo), te recomendaría que controles las liberaciones de contenidos de las referencias soft mediante una lista ReferenceQueue. Puedes pasar la lista como argumento del constructor de la softreference. Cuando se libera su contenido, la referencia se inserta en la lista automáticamente. Puedes repasarla periódicamente para notificar a la caché de sus contenidos inútiles o tomar las medidas necesarias.

      1. Tomo nota de tus palabras, se aprecia que compartas tus conocimientos!

        Gracias por todo

Replica a aironman2k Cancelar la respuesta