Una sola llamada al sistema para solicitar memoria al kernel puede tardar aproximadamente 610 nanosegundos, un coste que resultaría prohibitivo si un programa tuviera que realizar una por cada pequeña asignación. Para resolver esto, la función malloc actúa como un sofisticado gestor en el espacio de usuario que solicita grandes bloques de memoria y mantiene su propia contabilidad interna para atender las solicitudes más pequeñas de manera eficiente.

Esta tabla proporciona los datos empíricos que respaldan la afirmación de que malloc es significativamente más rápido que las llamadas directas al sistema.
El coste de la interacción con el kernel
Cuando un programa solicita memoria, esta reside en un espacio de direcciones dividido en regiones como texto, datos, bss, heap y pila. Aunque el kernel es el dueño final de la memoria física, llamar al kernel por cada pequeña asignación es ineficiente debido a la sobrecarga de cambiar del espacio de usuario al espacio del kernel.
Esta imagen ayuda a ilustrar las diferentes regiones del espacio de direcciones de un proceso, como el heap y la pila, mencionados en el texto.
Al solicitar un gran bloque de memoria a la vez —como un salto de 132 KiB en una sola operación—, malloc permite que un programa realice cientos de asignaciones posteriores sin más intervención del kernel. Esta estrategia garantiza que la mayoría de las solicitudes de memoria sean manejadas por la lógica interna de la biblioteca en lugar de por una lenta llamada al sistema.
Gráfico de HN Digest basado en cifras de benmandrew.com; los valores se muestran como en la fuente.
“malloc solicita grandes bloques al kernel y los corta por sí mismo.”
— What is malloc doing, anyway?
Contabilidad interna y listas de libres
Para gestionar estos grandes bloques, malloc utiliza 'fragmentos' que incluyen un encabezado de tamaño. Este encabezado permite al asignador saber cuánta memoria está ocupada y dónde comienza el siguiente fragmento sin que el usuario necesite proporcionar un tamaño durante la operación de liberación.

Este diagrama explica el mecanismo de contabilidad interna donde un encabezado precede a la memoria real asignada al usuario.
Cuando se libera la memoria, se coloca en una lista de libres. El asignador realiza entonces una 'división' para romper fragmentos libres grandes en piezas más pequeñas que se ajusten a solicitudes específicas, y una 'coalescencia' para fusionar fragmentos libres adyacentes de nuevo en bloques más grandes para evitar que la memoria se vuelva inutilizable.
El asignador garantiza que todos los punteros devueltos estén alineados a 16 bytes, que es el estándar para las arquitecturas x86-64, asegurando que la memoria sea compatible con cualquier tipo de datos en C.
Fragmentación y memoria residente
Una limitación significativa de la gestión del heap es la fragmentación. Debido a que el kernel solo puede reclamar memoria desde la parte superior del heap (el 'program break'), cualquier bloque de memoria activo situado por encima de un hueco libre impide que dicho hueco sea devuelto al sistema.
Esto significa que en programas de larga duración, la fragmentación puede causar que el tamaño del conjunto residente (RSS) crezca con el tiempo. Incluso si la aplicación 'libera' una gran cantidad de memoria, la RAM física permanece ocupada si incluso una sola asignación pequeña y activa se encuentra en una dirección superior al espacio libre.
Gestión de asignaciones grandes con mmap
Para solicitudes de memoria muy grandes, como aquellas que superan los 128 KiB, malloc puede cambiar de estrategia y utilizar la llamada al sistema mmap. Esto crea un mapeo completamente independiente en el espacio de direcciones del proceso que no forma parte del heap estándar.
A diferencia de la memoria del heap, estos bloques mapeados con mmap pueden devolverse al kernel inmediatamente tras ser liberados mediante la llamada al sistema munmap. Esto permite al asignador manejar asignaciones masivas sin contribuir a la fragmentación del heap ni impedir que el program break se reduzca.
Qué dicen los lectores de Hacker News
- Alternativa: Un comentarista expresó interés en los lenguajes 'solo de pila' como una forma de eliminar la complejidad y la sobrecarga asociadas con la abstracción de malloc. comentario ↗
- Escéptico: Un lector argumentó que inspeccionar malloc() no revela cómo se asigna la memoria realmente, ya que la lógica subyacente real reside en el kernel y en el firmware de los dispositivos. comentario ↗
- Experiencia: Un comentarista compartió que el uso de lenguajes con manejo explícito del asignador les ayudó a desarrollar mejores hábitos de programación al convertir la asignación de memoria en una elección de diseño deliberada. comentario ↗
- Experiencia: Otro usuario encontró útil estudiar el código fuente de malloc y escribir un asignador de juguete para comprender el puente entre la arquitectura de la computadora y el código de la aplicación. comentario ↗
Resumen de opiniones del hilo de Hacker News; no son hechos verificados. Leer el debate completo ↗