La gestión de memoria en C se describe como “explícita” porque tanto las operaciones de reserva como las de liberación han de aparecer explícitamente en el código. En otros lenguajes de programación, como por ejemplo Java, el entorno de ejecución se encarga de recuperar aquellas porciones de memoria que ya no se utilizan, liberando al programador de escribir este código. De la gestión explícita de memoria se derivan varias posibles anomalías cuando fragmentos de memoria no se pueden liberar porque se ha perdido cualquier referencia a ellos. Es lo que se conoce como “fuga de memoria”. Esa porción de memoria permanece reservada pero inaccesible para el resto de la ejecución de un programa.
Al comenzar la ejecución de un programa, su memoria se divide en tres zonas: la pila, memoria global y el “heap”. El heap se utiliza para la reserva y liberación de porciones de memoria durante la ejecución del programa. Pero ¿cómo se gestiona esta memoria?
El sistema operativo mantiene una tabla interna en la que
apunta qué fragmentos del heap están ocupados y qué punteros se han devuelto
como resultado de la petición de reserva. Cuando un programa ejecuta la
función malloc
para pedir un nuevo fragmento, el sistema busca
una porción del tamaño solicitado, si existe devuelve su dirección de
comienzo, y se apunta ese bloque como ocupado. De manera análoga, cuando se
llama a la función free
para liberar un fragmento, el sistema
busca en la tabla ese fragmento (que debe estar apuntado previamente como
reservado) y libera el espacio para usos futuros. En la siguiente figura se
ilustra este funcionamiento.
A la petición de reserva de una porción de 2048 bytes, el
gestor de memoria responde con la dirección de un bloque que previamente
marca como ocupado. La llamada a free
es análoga, pero se
recibe una dirección de memoria de un bloque previamente reservado, se busca
en la tabla, y si existe, se marca de nuevo como disponible.
Este esquema de gestión de memoria hace que los programas
deban ceñirse a unas pautas muy concretas para garantizar el correcto uso de
la memoria y sacar el mayor rendimiento a un programa. Por ejemplo, si un
programa utiliza una cantidad muy alta de datos dinámicos (esto es, que se
almacenan en la memoria solicitada al gestor mediante malloc
) y
no libera esa memora en cuanto puede, corre el riesgo de agotar la memoria y
no terminar la ejecución.