wandres.dev
ARQUITECTO · Gestión de memoria avanzada

Gestión de memoria avanzada

Más allá de malloc/free: arenas, pools y allocators a medida. Estrategias que eliminan fugas por diseño, reducen la fragmentación y aceleran el código real.

⏱ 15 min

malloc y free funcionan, pero en sistemas serios —motores de juego, compiladores, servidores— llamarlos por cada objeto es lento y propenso a fugas. Los programadores avanzados no gestionan la memoria objeto a objeto: la gestionan por regiones, con estrategias que hacen imposible la fuga.

🎯 Al terminar esta lección sabrás
  • Por qué malloc/free no siempre basta.
  • Arenas (region allocators): liberar todo de golpe.
  • Pools: objetos del mismo tamaño, rapidísimo.
  • Alineación y allocators a medida.

El problema de malloc por objeto

Llamar a malloc/free por cada objeto tiene tres costes: es lento (cada llamada busca un hueco), fragmenta el heap (huecos que no se reaprovechan), y es frágil (cada reserva es una oportunidad de olvidar el free). Para muchos patrones reales hay algo mejor.

Arenas: reservar mucho, liberar de golpe

Una arena (o region allocator) reserva un bloque grande una vez, y va entregando trozos de él con un simple avance de puntero. Cuando terminas, liberas la arena entera de una vez:

typedef struct {
    char  *base;
    size_t usado;
    size_t capacidad;
} Arena;

void *arena_alloc(Arena *a, size_t n) {
    if (a->usado + n > a->capacidad) return nullptr;
    void *p = a->base + a->usado;
    a->usado += n;
    return p;                    // asignar es solo avanzar un puntero
}

// uso: procesar una petición y tirar toda su memoria de golpe
Arena a = { malloc(1 << 20), 0, 1 << 20 };  // 1 MB
// … miles de arena_alloc, sin un solo free individual …
free(a.base);                    // se libera TODO de una vez
Las arenas hacen imposible la fuga

Aquí está la genialidad: en una arena, no hay free por objeto — no puedes olvidarlo porque no existe. Asignar es incrementar un puntero (la operación más rápida posible), y liberar es tirar el bloque entero. Esto encaja perfectamente con patrones de “vida por fases”: todo lo que reservas para procesar una petición web, un frame de un juego o compilar un archivo, muere junto al final de esa fase. Los compiladores, los navegadores y los motores de juego usan arenas masivamente. Cambia tu mentalidad de “gestiono cada objeto” a “gestiono el ciclo de vida de un grupo de objetos”, y clases enteras de bugs de memoria desaparecen.

Pools: objetos del mismo tamaño

Un pool (o free list) preasigna muchos objetos idénticos y mantiene una lista de los libres. Reservar y liberar es enlazar/desenlazar — O(1) y sin fragmentación, porque todos los huecos son iguales:

// se reserva un bloque de N nodos; los libres forman una lista enlazada.
// alloc = quitar el primero de la free list; free = volver a ponerlo.
Nodo *nodo_alloc(Pool *p);   // O(1), sin buscar
void  nodo_free(Pool *p, Nodo *n);

Ideal para estructuras con muchos elementos del mismo tipo (nodos de árbol, partículas, entradas de tabla).

Alineación

Cierta memoria debe estar alineada a fronteras concretas (SIMD, DMA, hardware). C11/C23 lo dan estándar:

#include <stdlib.h>
void *p = aligned_alloc(64, 1024);   // 1024 bytes alineados a 64 (línea de caché)
free(p);
alignas(16) float vec[4];            // variable alineada a 16 bytes

Allocators a medida

💡
La memoria es una decisión de diseño, no un detalle

La lección profunda de este nivel: en programación de sistemas de alto rendimiento, la estrategia de asignación es parte del diseño de tu programa, no una función de librería que llamas sin pensar. ¿Los objetos viven y mueren en grupo? Arena. ¿Muchos del mismo tamaño, vida individual? Pool. ¿Uno grande y de vida larga? Un malloc normal está bien. Elegir la estrategia según el patrón de vida de tus datos es lo que da el salto de rendimiento y robustez que separa el código amateur del profesional. En el nivel 30 (Dios) escribirás un allocator completo desde cero.

⚔️ Piensa por regiones
  1. Implementa una arena mínima (base, usado, capacidad) con arena_alloc.
  2. Úsala para asignar muchos objetos y libéralos todos con un solo free.
  3. Usa aligned_alloc para reservar memoria alineada a 64 bytes.
  4. Piensa en un programa tuyo: ¿qué datos podrían gestionarse con una arena en vez de malloc/free individuales?