Pobreza en la abundancia, la memoria olvidada
En la universidad, aprendí sobre la ‘Estructura de Memoria’ en las clases de Sistemas Operativos. Recuerdo vagamente términos como Stack y Heap apareciendo en los exámenes.
Pero siendo honesto, hasta que me gradué, casi nunca me preocupé seriamente por la memoria. Vivimos en una era donde las laptops personales tienen 16GB o 32GB de RAM por defecto. Haciendo tareas de nivel universitario, era prácticamente imposible quedarse sin memoria. Además, lenguajes como Java tienen un Garbage Collector (GC) que limpia por ti, así que no sentía la necesidad de calcular direcciones de memoria.
Así, embriagado por la abundancia del hardware y la comodidad del lenguaje, me lancé al trabajo real habiendo olvidado el ‘sentido de la memoria’, una cualidad básica de todo desarrollador.
La dura lección del mundo real
Cuando practicaba para las pruebas de código en la escuela, lo máximo que hacía era meter algunos números en un array y procesarlos. Los datos de entrada rara vez superaban los 100,000 elementos. Pero el trabajo real fue diferente. Incluso en una startup pequeña, la base de datos (DB) acumulaba millones de datos de clientes reales.
En el trabajo, para manejar estos datos masivos cómodamente, usamos una tecnología llamada ORM (Object Relational Mapping). Es una herramienta muy agradecida que permite manejar datos como si fueran objetos Java sin escribir consultas SQL a mano. (En el mundo Java, Hibernate es el representante).
El problema era que esta herramienta es ‘demasiado cómoda’.
Como podía traer una lista de datos de la DB con solo presionar un botón, no pude medir el peso de los datos ocultos detrás. Escribí una sola línea de código diciendo «Trae la información de los clientes», y terminé cargando en la memoria la información de decenas de miles de personas y todo su historial de pedidos.
El resultado fue desastroso. Los 32GB de RAM se llenaron en un instante. El servidor sufrió un ‘Lag extremo’ debido al GC (el conserje) intentando liberar memoria desesperadamente, y finalmente, se detuvo.
Stack familiar, Heap desconocido
Analizando los logs de error que el servidor escupió, mis ojos se clavaron en una frase:
java.lang.OutOfMemoryError: Java heap space
La palabra ‘Stack’ me resultaba bastante familiar. La estudié hasta el cansancio en clases de Estructuras de Datos, y es el nombre del sitio que visitamos a diario (Stack Overflow). También sabía por sentido común que si escribes mal una función recursiva, el Stack explota.
Pero el culpable que mataba mi servidor en el trabajo no era el Stack. Los logs siempre señalaban al ‘Heap’.
«¿No está lleno el Stack, sino que falta espacio en el Heap?»
La duda cruzó mi mente. Entiendo el Stack, pero ¿qué diablos es el Heap para que falle tan seguido en producción? ¿Es lo mismo que la estructura de datos Heap? ¿Por qué mi código tortura al Heap y no al Stack?
Esa duda me llevó de nuevo a los libros de texto polvorientos y al mundo de Google. Y así descubrí que la ‘Memoria (RAM)’, la casa donde vive mi código, no es un solo cuarto gigante, sino un ‘espacio dividido’ estrictamente según su propósito.

La mesa de trabajo y el almacén del Centro Logístico Digital
Volvamos a nuestra metáfora del ‘Centro Logístico Digital’.
Aquí, la ‘RAM (Memoria)’ es el espacio donde el trabajador (CPU) despliega las cosas para trabajar. Pero este espacio se opera dividido estrictamente en dos para la eficiencia.
1. Stack (Pila): La mesa de trabajo personal
- Característica: Es un escritorio estrecho y alto justo enfrente del trabajador (CPU).
- Uso: Se colocan temporalmente solo las variables (variables locales, parámetros) necesarias para la tarea (función) que se está realizando ahora mismo.
- Vida útil: Cuando termina la tarea (función), se limpia la mesa por completo (Pop). Es muy fácil y rápido de gestionar.
- Metáfora: Los ingredientes en la tabla de picar mientras cocinas. Al terminar de cocinar, se limpian de inmediato.
2. Heap (Montón): El almacén general
- Característica: Es un almacén gigante detrás de la mesa de trabajo. Es espacioso, pero toma tiempo ir a buscar las cosas.
- Uso: Almacena datos grandes o que deben guardarse por mucho tiempo (Objetos, Instancias).
- Vida útil: Permanecen allí hasta que el usuario los tira o el conserje (Garbage Collector) los limpia.
- Metáfora: El refrigerador o la despensa. Los ingredientes siguen allí aunque termines de cocinar.

[Code Verification] Dos mundos probados con errores
¿Realmente la memoria está dividida así? Podemos probar la existencia de estos dos espacios provocando errores intencionalmente con código.
1. Reventar el Stack (StackOverflowError)
Dije que el Stack es una ‘mesa de trabajo’. La mesa es estrecha. Si una función no termina y sigue llamándose a sí misma (recursión), los documentos se apilan hasta el techo y la mesa colapsa.
public class StackTest {
public static void recursiveCall(int depth) {
// Llamada recursiva infinita: La función no termina y se sigue apilando en el Stack
System.out.println("Stack Depth: " + depth);
recursiveCall(depth + 1);
}
public static void main(String[] args) {
recursiveCall(1);
}
}
Resultado: Después de unas miles de vueltas, el programa muere escupiendo un StackOverflowError. Aunque el espacio del Heap esté totalmente vacío, si el Stack (mesa) se llena, el programa muere.
2. Reventar el Heap (OutOfMemoryError)
Ahora recreemos la pesadilla que viví. Similar a cargar decenas de miles de objetos a la vez usando mal un ORM, vamos a crear listas gigantes y meterlas a la fuerza en el almacén (Heap).
import java.util.ArrayList;
import java.util.List;
public class HeapTest {
public static void main(String[] args) {
List<byte[]> warehouse = new ArrayList<>();
while (true) {
// Seguir creando datos de 1MB y apilarlos en el Heap
// Ejemplo real: Ocurre al consultar miles de datos de la DB sin paginación
warehouse.add(new byte[1024 * 1024]);
}
}
}
Resultado: java.lang.OutOfMemoryError: Java heap space. Esto no es problema de la mesa de trabajo (Stack). Es un error que ocurre porque no hay espacio para estibar más mercancía en el almacén (Heap). Es el momento en que confirmas con tus propios ojos cómo tu código tortura al Heap.
La existencia del conserje: Garbage Collector (GC)
Aquí hay una diferencia importante. El Stack se vacía ‘automáticamente’ cuando termina la función. No hay que preocuparse. Pero en el Heap, si alguien no limpia, la basura se sigue acumulando.
En lenguajes antiguos como C, el desarrollador tenía que limpiar manualmente con el comando free(). Si se te olvidaba, el almacén se llenaba de basura y explotaba (Memory Leak). En cambio, lenguajes modernos como Java, Python o JS contratan a un conserje profesional llamado ‘GC (Garbage Collector)’.
«¿Este objeto ya no lo usa nadie?»
El GC recorre periódicamente el Heap, encuentra objetos sin dueño y los tira. Gracias a esto, no tenemos que escribir código para liberar memoria. Pero nada es gratis. En el momento en que el GC hace una limpieza general, todo el trabajo en el centro logístico se detiene momentáneamente (Stop-the-world). Ese fenómeno donde el juego se congela de repente o el servidor se detiene por 1 segundo, es justamente por este tiempo de limpieza.
Conclusión: Ojos para ver lo invisible
Después de entender Stack y Heap, el código en mi monitor comenzó a verse diferente.
Antes, al ver el código new Student(), simplemente pensaba «Ah, creé un objeto». Pero ahora lo veo: «Acaba de entrar una caja al almacén llamado Heap. Si no borro esto (o si el GC no viene), seguirá comiéndose la memoria».
Desde que desarrollé estos ‘ojos para ver lo invisible’, pude escapar del miedo vago. Incluso si el servidor escupe un OutOfMemory, ya no presiono el botón de reinicio presa del pánico como antes. En su lugar, abro las herramientas de análisis preguntando con calma: «¿Qué objeto ha invadido el Heap?». Porque si conoces la causa, puedes resolverlo.
Ahora hemos conquistado el espacio (Memoria) donde se guarda el código. El almacén logístico está perfectamente listo. Entonces, ¿quiénes son los ‘trabajadores’ que realmente mueven y ensamblan las cosas en este almacén? ¿Cuál es la diferencia entre trabajar solo y trabajar varios a la vez (Multi-Tasking)?
En el próximo artículo, viajaremos al mundo de ‘Procesos e Hilos (Process & Thread)’, la flor de los Sistemas Operativos y la eterna tarea de los desarrolladores Backend.