Como ya dijimos en un post anterior, uno de los aspectos más importantes en todo lenguaje de programación es cómo se gestiona la memoria. Es decir cuándo, cómo y quién libera los objetos que se van creando durante la ejecución del programa. En dicho post explicamos la casuística de la gestión manual de la memoria, la cual es una de las cuatro grandes técnicas de gestión de la memoria, a saber: gestión manual, garbage collector, contador de referencias manual y contador de referencias automático. En el post de hoy nos centraremos en la técnica garbage collector.
Garbage collector
En el extremo opuesto a la gestión manual se encuentran los lenguajes que tienen garbage collector (Recolector de elementos no utilizados). En este caso toda la gestión de la memoria es automática: el programador crea objetos pero nunca debe destruirlos, ya que el sistema lo hace por él. Sus defensores afirman que la gestión de memoria es tan importante que no puede ser dejada al desarrollador y debe efectuarla el sistema.
A pesar de que la idea de que no tener que preocuparnos de la destrucción de objetos suena (y funciona) bastante bien, no es la panacea. Veamos algunas razones:
- La abstracción del garbage collector fuga en cuanto tenemos que acceder a recursos que no puede “manejar” nuestro tiempo de ejecución (runtime).
Un caso típico son las conexiones a BBDD: si abrimos una conexión a BBDD debemos cerrarla tan pronto terminemos para evitar saturar al servidor de BBDD. Así p. ej. para usar una conexión a una BBDD en un lenguaje con garbage collector como C# tendríamos un código parecido al siguiente:
SqlConnection con = null;
try {
con = new SqlConnection(constr);
con.Open();
// operar con la conexión
}
finally {
if (con != null) con.Dispose();
}
En C# se usa un interfaz (IDisposable) que es implementado por todas las clases que operan con recursos externos. El desarrollador debe llamará a Dispose() para indicar al sistema que puede liberar los recursos ocupados; en este caso la conexión física con el servidor de BBDD.
Si una clase usa miembros que sean IDisposables, la clase debe implementar a su vez IDisposable y llamar a Dispose() de todos sus miembros en su propio método Dispose. Este comportamiento recuerda al del destructor de lenguajes como C++, pues Dispose() es, simplemente, como un destructor de la parte que el garbage collector no puede manejar.
En Java son los interfaces AutoCloseable y Closeable los que juegan el rol de IDisposable, pero la idea es la misma.
- Otro de los problemas del garbage collector es que no es determinista: No hay manera de saber cuándo se va a liberar la memoria y una vez que se libere tampoco hay manera de saber cuánta se va a liberar.
Piensa que el garbage collector es un componente del runtime que, bajo ciertas condiciones, se pone en marcha, analiza qué objetos ya no se usan (es decir no hay ninguna referencia válida que apunte a ellos) y los destruye (todos o algunos).
- Y eso trae a colación el tercer problema del garbage collector, y es que mientras aquél se ejecuta nuestro código está parado: Es decir, cada cierto tiempo la ejecución de nuestro programa se interrumpe durante un lapso para que el garbage collector tenga tiempo de realizar su tarea.
Por supuesto en los garbage collectors este tiempo puede llegar a ser mínimo, ya que se ejecutan en su propio hilo. Pero incluso en este caso, es posible que nuestro código se llegue a detener momentáneamente ya que una de las tareas que el garbage collector puede hacer es compactar o desfragmentar la memoria. Eso implica mover objetos físicamente de sitio, y cuando eso ocurre las referencias afectadas deben ser recalculadas y por lo tanto código que dependa de ellas no puede ejecutarse hasta que dicho proceso haya finalizado. De todos modos esos dos últimos problemas (no determinismo y rendimiento) no son realmente significativos.
Por poco que el desarrollador sea cuidadoso en los puntos en que la abstracción del garbage collector fuga, la realidad es que el garbage collector funciona realmente bien. Es cierto que es posible que incluso con garbage collector se den memory leaks pero en general los beneficios son muy superiores a sus inconvenientes.
En un próximo post estudiaremos la gestión de memoria por contador manual de referencias.
Fecha de publicación: