Los tipos de referencias débiles soft, weak y phantom en Java

Publicado por pico.dev el , actualizado el .
blog-stack java planeta-codigo programacion
Comentarios

Java

Cuando un objeto ya no es alcanzable a través de ninguna referencia directa o cadena de referencias fuertes el objeto es seleccionable para reclamar su memoria y el recolector de basura o garbage collector de Java lo hace cuando estima oportuno, liberándonos a los programadores de esta tarea, simplificando el código y evitando fugas de memoria. El lenguaje Java le debe al recolector de basura entre otras varias cosas una buena parte de su éxito.

En Java en realidad hay 4 tipos de referencias a objetos, además de las fuertes hay otras 3 más débiles que no impiden al recolector de basura reclamar el objeto referenciado. Es raro tener la necesidad de usar otra que no sean las fuertes o strong pero es interesante conocerlas por si en algún caso nos resultase de utilidad. Los otros 3 tipos de referencias denominadas débiles son SoftReference, WeakReference y PhantomReference que extienden de Reference. Usar una de estas otras 3 referencias es muy simple basta usar el constructor de cada tipo de referencia.

Después de la llamada de varias veces al recolector de basura en este caso de forma explícita con el método System.gc() las referencias son encoladas.

El objeto de una referencia soft es recolectable a discreción del recolector de basura ante necesidades de memoria, el objeto de una referencias weak es recolectable si solo es alcanzable por referencias weak y las referencias phantom son una mejor y más flexible alternativa al mecanismo de finalización de los objetos.

Algunos usos prácticos de las referencias soft y weak son como caches de datos posiblemente usando la clase WeakHashMap, en el caso de las referencias phantom como mecanismo alternativo a la finalización de objetos incorporada en los objetos desde la versión inicial de Java.

El mecanismo de finalización de los objetos Java con el método finalize que puede ser implementado por cualquier clase presenta los siguientes problemas:

  • La llamada al método finalize es impredecible ya que depende de cuando del recolector de basura reclame el objeto.
  • No hay garantía de que el método finalize sea llamado ya que puede perdurar durante toda la vida de la JVM.
  • Una referencia fuerte al objeto puede ser revivida en el método finalize si se implementa de forma inadecuada.

En los constructores de las referencias débiles se puede indicar un ReferenceQueue en el que se encolará la referencia cuando el objeto al que referencia cambia su alcanzabilidad. Este mecanismo de notificación es utilizado con las referencias phantom para proporcionar el mecanismo de finalización alternativo. En la documentación javadoc con la descripción del paquete de las referencias se comenta este proceso de notificación. Las referencias son encoladas cuando el recolector de basura determina que son solo alcanzables por referencias soft, weak o phantom.

En el artículo Replacing Finalizers With Phantom References se explica junto con su código como implementar el mecanismo alternativo al método finalize. La librería Guava proporciona las clases FinalizablePhantomReference y FinalizableReferenceQueue con una forma un poco más sencilla de usar las referencias phantom, en esa documentación también hay un ejemplo de código con su uso para liberar un recurso (ServerSocket) asociado a un objeto (MyServer).

Las referencias débiles añaden una indirección a la referencia que contienen, usando el método get() se accede al objeto referenciado pero hay que tener en en cuenta que el método get puede devolver un null ya que no impiden al recolector de basura reclamar el objeto referenciado, en el caso de las PhantomReferences el método get siempre devuelve null para evitar que la referencia a un objeto sea revivida.

Otro artículo que recomiendo leer es Weak, Soft, and Phantom References in Java (and Why They Matter), explica el concepto de estas referencias con un símil más fácil de comprender de un restaurante y sus clientes que dependiendo de su comportamiento se asemeja a estas referencias y el por que de los recolectores de basura, que no es algo novedoso de Java sino que ya fué utilizado en 1959 con el lenguaje Lisp.

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando el comando ./gradlew run.