Gestión de la memoria en .NET y garbage collector

La tecnología .NET consta de una gestión automática de memoria que permite liberar segmentos de memoria que no se utilizan. Este sistema de liberación de memoria se conoce como recolector de basura o garbage collector (en adelante GC), y a excepción de Java (y algún otro lenguaje más) esta gestión automática no se encuentra en otros lenguajes de programación. Esta gestión automática permite un uso mucho más eficiente y cómodo de los recursos por parte del usuario (programador).

Aunque existe el concepto de destructor en .NET y es posible tocar ciertos parámetros de la gestión de la memoria, no es recomendable hacerlo si no se sabe bien lo que se está haciendo; los algoritmos de gestión de memoria y  recolección de basura están suficientemente optimizados como para tener que tocar nada al respecto. Sin embargo sí que existen algunas buenas prácticas de programación que permiten aumentar la eficiencia del recolector de basura.

En este artículo veremos cómo .NET organiza la memoria de un programa y cómo trabaja el GC. En el siguiente artículo veremos el concepto de “generación” del GC, que permite promocionar objetos que se usan con más frecuencia por parte de la aplicación.

Fundamentos y necesidad

Todos los programas utilizan recursos de la máquina, sean éstos ficheros, búffers de memoria, espacio en pantalla, conexiones de red o de base de datos. Una vez que ya no se usan estos recursos, es necesario liberarlos o la aplicación puede, en algún momento dado, dejar sin recursos a la máquina para otros programas. La cuestión es saber cuándo no se van a usar más dichos recursos. En algunas ocasiones es relativamente sencillo, como por ejemplo una conexión
de red, de base de datos o un fichero: se crea la conexión en un bloque try y se libera en un bloque finally. O mejor aún: si está escribiendo código en C# utilice la sentencia using (veremos el uso de esta sentencia en el siguiente artículo).

Sin embargo, en otras ocasiones no es tan sencillo, como por ejemplo la gestión de la memoria.  ¿Cuándo deja de ser útil un objeto en memoria?

Montículo y pila

Aquellos que conozcan mínimamente la tecnología .NET sabrán que las “variables” en .NET tienen dos tipos fundamentales: valores tipo y valores referencia.

Los valores tipo son los que comunmente conocemos como tipos simples (int, char, long, byte…) y en memoria se almacenan en una estructura de pila.

Los valores referencia son los que comunmente conocemos como objetos (además de strings y arrays), que tienen cierta complejidad y en memoria se almacenan en una estructura de montículo.

El GC actúa sobre el montículo y no sobre la pila porque los segmentos almacenados en la pila son pequeños y de un tamaño controlado, mientras que en el montículo se almacenan bloques de memoria de distinta índole y tamaño.

Gestión básica de la memoria

Si representamos el montículo como una lista, los objetos se van almacenando en dicho montículo de forma lineal, existiendo un puntero, al que denominaremos NextObjPtr que apunta a la primera dirección libre de memoria reservada para la aplicación en ejecución.

Cada vez que se crea un nuevo objeto, se calcula su tamaño, se añade al montículo y se desplaza el puntero tanto como tamaño tenga el nuevo objeto. Es decir, toda la gestión de memoria se realiza mediante un puntero. Sencillo y muy eficiente, ¿verdad? ¿Cómo es que a nadie se le ha ocurrido antes? Bueno, es que esta aproximación tiene una pega importante, y es que está presuponiendo una cantidad de memoria infinita…

El recolector de basura (GC)

¿Qué ocurre en un entorno real en el que la memoria es limitada? Existe un algoritmo que permite determinar cuándo es necesario liberar memoria y qué es lo que hay que liberar. Esto es el GC.

¿Cómo determina el GC qué se puede liberar? Aquí entra en juego el concepto de raíz o referencia fuerte. Una raíz es: una variable gobal, un objeto estático, una variable local o un registro de CPU (aquí estamos hablando desde el punto de vista del lenguaje intermedio (IL) de .NET, no de un lenguaje de alto nivel). Una raíz no se puede eliminar de memoria, porque se entiende que se puede utilizar en cualquier momento.

Inicialmente, el GC asume que todo es basura y susceptible de ser recolectado. Enconces recorre el montículo buscando raíces. Estas raíces no se eliminarán en la próxima pasada del GC.

Con ello, tendremos un grafo con elementos directamente accesibles desde el código y otros que no. Ahora, el GC introduce el concepto de “alcanzable”, que son aquellos objetos accesibles a través de una raíz. Dichos objetos tampoco son eliminables. Se trata de variables que son accesibles desde el ámbito de uso de las raíces. Puesto que las raíces no son eliminables, los alcanzables tampoco.

Una vez completado este grafo, existirán objetos que no están apuntados por nadie. Éstos objetos son candidatos a ser eliminados en la próxima pasada del GC.
Esto ocurrirá cuando se detecte que la memoria se está acabando. Cuando esto suceda, los objetos que no se usan serán eliminados, los bloques de memoria utilizada serán compactados y se actualizará el NextObjPtr para apuntar a la nueva primera dirección de memoria libre para asignar.
Un detalle importante a tener en cuenta es que la eliminación de objetos no es determinística: un usuario no sabe realmente cuándo un elemento se va a eliminar; un objeto se eliminará… cuando se determine que no se va a usar más y cuando sea necesario liberar memoria.

En el próximo artículo explicaré los destructores (más propiamente llamados en terminología .NET finalizadores) y las generaciones del GC y cómo controlarlas desde un programa. También apuntaré algunas prácticas que pueden ayudar a sacar provecho al GC. Nos leemos.


Applied Microsoft .NET Framework programming, Jeffrey Richter
CLR via C#, Jeffrey Richter

Un pensamiento en “Gestión de la memoria en .NET y garbage collector

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s