🧠
Avanzado
12 min lecturaGestión de memoria y recolección de basura
Comprende fugas de memoria, heap, stack y recolección de basura en JavaScript
Gestión de memoria y recolección de basura
Comprender cómo JavaScript gestiona la memoria es crucial para crear aplicaciones rápidas. Aprende sobre el heap, el stack, la recolección de basura y cómo prevenir fugas de memoria.
1. Memoria de stack vs heap
Memoria de stack:
- Almacena valores primitivos (números, cadenas, booleanos)
- Almacena frames de llamadas y variables locales
- Acceso rápido, limpieza automática cuando la función termina
- Tamaño limitado (típicamente 1MB)
- Estructura LIFO (Last In, First Out)
- Almacena objetos, arrays y funciones
- Mayor tamaño, acceso más lento que el stack
- Requiere recolección de basura para liberar memoria
- Las variables guardan referencias a ubicaciones en el heap
// Stack memory
let x = 10; // Primitive stored on stack
let y = 20;
let sum = x + y; // Result on stack
// Heap memory
let obj = { name: 'John', age: 30 }; // Object on heap
let arr = [1, 2, 3]; // Array on heap
// obj and arr variables (references) are on stack
// Reference example
let a = { value: 5 };
let b = a; // b references same object
b.value = 10;
console.log(a.value); // 10 - both reference same object
2. Recolección de basura
JavaScript usa recolección automática de basura para liberar memoria. Los dos algoritmos principales:
Algoritmo mark-and-sweep (enfoque moderno):
// Root objects (global, currently executing functions)
// are marked as "active"
// GC traverses and marks all reachable objects
// Unreachable objects are swept (deleted)
function createUser() {
let user = { name: 'John' }; // Created
return user;
}
let activeUser = createUser(); // user object is reachable
activeUser = null; // Now unreachable, will be garbage collected
// Example: Circular references (handled by mark-and-sweep)
let obj1 = {};
let obj2 = {};
obj1.ref = obj2;
obj2.ref = obj1;
obj1 = null;
obj2 = null;
// Both objects are now unreachable and will be GC'd
3. Fugas de memoria comunes
Fuga #1: Variables globales
// ❌ Bad: Accidental global
function leak() {
user = { name: 'John' }; // No let/const, becomes global
}
// ✅ Good: Use strict mode
'use strict';
function noLeak() {
let user = { name: 'John' }; // Properly scoped
}
Fuga #2: Event listeners
// ❌ Bad: Listener never removed
function setupListener() {
const button = document.querySelector('#btn');
button.addEventListener('click', () => {
console.log('Clicked');
});
}
// ✅ Good: Remove listener
function setupListener() {
const button = document.querySelector('#btn');
const handler = () => console.log('Clicked');
button.addEventListener('click', handler);
// Cleanup
return () => button.removeEventListener('click', handler);
}
// React example
useEffect(() => {
const handler = () => console.log('Scroll');
window.addEventListener('scroll', handler);
return () => window.removeEventListener('scroll', handler);
}, []);
Fuga #3: Timers
// ❌ Bad: Timer never cleared
function startTimer() {
setInterval(() => {
console.log('Tick');
}, 1000);
}
// ✅ Good: Clear timer
function startTimer() {
const timerId = setInterval(() => {
console.log('Tick');
}, 1000);
return () => clearInterval(timerId);
}
// React example
useEffect(() => {
const timerId = setInterval(() => {
console.log('Tick');
}, 1000);
return () => clearInterval(timerId);
}, []);
Fuga #4: Closures que mantienen referencias
// ❌ Bad: Closure holds large object
function createHandler() {
const largeData = new Array(1000000).fill('data');
return function() {
console.log(largeData[0]); // Holds entire array
};
}
// ✅ Good: Extract only what you need
function createHandler() {
const largeData = new Array(1000000).fill('data');
const firstItem = largeData[0];
return function() {
console.log(firstItem); // Only holds one item
};
}
Fuga #5: Elementos DOM desconectados
// ❌ Bad: Keeping reference to removed DOM element
let elements = [];
function addElement() {
const div = document.createElement('div');
document.body.appendChild(div);
elements.push(div); // Keep reference
}
function removeElement() {
document.body.removeChild(elements[0]);
// Element removed from DOM but still in memory (elements array)
}
// ✅ Good: Remove all references
function removeElement() {
const element = elements.shift();
document.body.removeChild(element);
// No more references, can be GC'd
}
4. WeakMap y WeakSet
Usa WeakMap y WeakSet para objetos que no quieres impedir que sean recolectados por el GC.
// Regular Map prevents GC
const map = new Map();
let obj = { data: 'important' };
map.set(obj, 'metadata');
obj = null; // Object NOT garbage collected (Map still holds it)
// WeakMap allows GC
const weakMap = new WeakMap();
let obj2 = { data: 'important' };
weakMap.set(obj2, 'metadata');
obj2 = null; // Object CAN be garbage collected
// Practical use: Private data
const privateData = new WeakMap();
class User {
constructor(name) {
privateData.set(this, { password: 'secret' });
this.name = name;
}
getPassword() {
return privateData.get(this).password;
}
}
const user = new User('John');
console.log(user.name); // Accessible
console.log(user.password); // undefined (private)
console.log(user.getPassword()); // 'secret'
5. Perfilado de memoria
// Check memory usage
if (performance.memory) {
console.log('Heap size:', performance.memory.totalJSHeapSize);
console.log('Used heap:', performance.memory.usedJSHeapSize);
console.log('Heap limit:', performance.memory.jsHeapSizeLimit);
}
// Chrome DevTools:
// 1. Open DevTools → Memory tab
// 2. Take heap snapshot
// 3. Compare snapshots to find leaks
// 4. Look for detached DOM nodes
// 5. Check objects in memory
// Force garbage collection (DevTools only)
// Performance → Memory → Collect garbage icon
Buenas prácticas de optimización de memoria:
- ✓ Limpia siempre event listeners, timers y suscripciones
- ✓ Evita variables globales
- ✓ Ten cuidado con closures: no captures datos innecesarios
- ✓ Elimina referencias a elementos DOM cuando los quites
- ✓ Usa WeakMap/WeakSet para cachés ligados a objetos
- ✓ Perfila memoria de forma regular durante el desarrollo
- ✓ Pon en null objetos grandes cuando termines
- ✓ Usa object pooling para objetos creados/destruidos frecuentemente