Tipos de referencias en Java

Estándar

De cualquier programador que clame conocer Java, se espera que esté al tanto de la Máquina Virtual de Java (JVM) y de la existencia y función del Recolector de Basura (Garbage Collector). Se sabe que el Recolector de Basura libera la memoria ocupada por todos aquellos objetos que son elegibles para tal acción. Por cuestiones de simplicidad, desde el inicio del aprendizaje de Java se aprende que un objeto es elegible para ser recolectado (y liberado) cuando no existe ninguna referencia apuntando a dicho objeto. Sin embargo, lo que no nos dicen es que existen más de un tipo de referencia y la elegibilidad está basado en estos diferentes tipos. Este es un hecho que ni siquiera en los exámenes de certificación OCA/OCP mencionan. Es ese tipo de conocimiento que se debe aprender investigando más a fondo acerca de la JVM y el Recolector de Basura.

En total son cuatro tipos de referencias las que maneja el Garbage Collector. Strong reference, Soft reference, Weak reference, y Phantom reference. Estas referencias (excepto la Strong porque es implícita) están definidas en el paquete java.lang.ref y se usan como contenedores de referencias a objetos.

Strong Reference

Este es el tipo de referencia más simple y la que todos los programadores usan en el día a día.

String ref = new String("GuaJava"); //ref es una Strong reference

Un StrongReference impide que el Garbage Collector reclame la memoria ocupada por el objeto al que se hace referencia. Mientras un objeto tenga al menos una referencia (Strong) apuntando hacia él, tal objeto no será elegible para ser reclamado.

Soft Reference

Este tipo de referencia puede ser liberada de la memoria luego de que no existan ningún Strong Reference apuntando hacia el objeto. Sin embargo, esta acción sólo se llevará acabo cuando sea absolutamente necesario, como cuando los niveles de memoria disponibles estén bajos, es decir, todos los Soft References serán reclamados antes de que ocurra un OutOfMemoryException.

String ref = new String("GuaJava"); //ref es una Strong reference
SoftReference<String> softRef = new SoftReference<>(ref); //Weak Reference
System.out.println(softRef.get()); //imprime el valor de ref 
ref = null; // ahora ref es elegible para ser reclamado por el GC, pero sera liberado solamente si el JVM necesita memoria

Por su naturaleza, este tipo de referencia puede usarse para implementar sistemas de caché de objetos que pueden recrearse de ser necesarios.

Weak Reference

Este tipo de referencia puede ser liberada de la memoria cuando no existan ningún Strong Reference ni Soft Reference apuntando hacia el objeto. Sin embargo, a diferencia de las Soft Reference, el GC puede liberar la memoria en cualquier momento aún si no hay escasez de memoria. Las Weak References son recolectadas de manera Eager mientras las Soft References son recolectadas de manera Lazy.

String ref = new String("GuaJava"); //ref es&nbsp;una Strong reference
WeakReference<String> weakRef = new WeakReference<>(ref); //Weak Reference
System.out.println(weakRef.get()); //imprime el valor de ref 
ref = null; // ahora ref es elegible para ser reclamado por el GC
System.gc(); //Tratamos de llamar al GC. No hay garantía de que funcione
System.out.println(weakRef.get()); //podrial imprimir null si ref fue reclamado

Un buen ejemplo de este tipo de referencias es una implementación de la interfaz Map: WeakHashMap. Una vez la clave de una entrada de esta implementación pierde la Strong Reference, la entrada es elegible para ser reclamada por el GC.

Phantom Reference

Este tipo de referencia al igual que la WeakSoft no impide que la memoria ocupada por el objeto referencia sea reclamada por el GC. Este tipo de referencias se usan principalmente como método alternativo al llamado finalize() de los objetos por parte del GC, la cual se considera una mala práctica. Primero, vamos a hablar un poco, rápidamente, sobre los problemas de la finalización de los objetos a través del método finalize().

La llamada al método finalize() de los objetos, se supone es para la limpieza/liberación de los recursos utilizados por el objeto cuando dicho objeto no sea necesario y vaya a ser eliminado de la memoria. Sin embargo, este método tiene algunos problemas.

Primeramente, no se puede anticipar cuándo será finalizado el objeto por parte del GC, ya que la ejecución del GC es impredecible, también lo es la llamada a finalize(). Tampoco hay garantías de que el objeto sea elegible para ser reclamado por el GC, podría nunca ser elegible y por lo tanto nunca llamar al método finalize(), nunca así liberando los recursos que este tiene. También es posible que el GC nunca se ejecute desde el momento en que el objeto es elegible para ser reclamado hasta el momento en que el JVM termina.

Segundo, la finalización puede poner lenta la aplicación, ya que el GC demora más tiempo gestionar los objetos que tienen que hacer algo desde finalize().

Por estas razones, no tiene mucho sentido hacer la limpieza de los recursos desde este método. De hecho, está contraindicado. Si se requiere realizar algunas rutinas de limpieza, la recomendación es ejecutarlas una vez se terminen de usar los recursos, pero esto no necesariamente es así.

//las referencia de los objetos seran almacenadas
//en esta cola cuando sean finalizados para acciones de limpieza
ReferenceQueue refQueue = new ReferenceQueue(); 

String ref = new String("GuaJava");
PhantomReference<String> phantom = new PhantomReference<>(ref, refQueue);

Phantom Reference se puede usar cuando finalize no es adecuado. Se puede usar en conjunto con ReferenceQueue, de manera que se pueda recibir una “notificación” cuando un objeto ha sido finalizado pero aun no reclamado por el GC. De esta manera, se pueden tomar acciones de limpieza de una manera segura y controlado por la aplicación, y no depender del GC para tales acciones.

Conclusiones

Hemos visto los diferentes tipos de referencias en Java, y como estas afectan el funcionamiento en el manejo de la memoria por parte del GC. También vimos que el GC tiene unos criterios de elegibilidad para reclamación de memoria un poco más complejos de lo que nos imaginábamos o de lo que nos habías enseñado hasta ahora. Esto nos da un poco más de flexibilidad en cuanto a la administración de la memoria por parte del GC.

Si te gustó, ¡comparte!Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedInShare on Reddit