JavaScript manages memory automatically. The engine allocates memory when you create values (numbers, strings, objects, functions, etc.) and frees it when those values are no longer needed. This automatic cleanup process is called garbage collection.
As a developer, you don’t manually free memory, but your code still strongly influences how efficiently memory is used. Poor patterns can cause memory leaks, leading to slow, bloated apps.
Memory is allocated whenever you create new values:
JavaScript engines (like V8) use a reachability-based algorithm: anything that can be reached from the roots (global scope, current stack, and active closures) is considered “alive”. Anything else becomes eligible for garbage collection.
Memory problems typically arise when we accidentally keep references to data we don’t need anymore—for example through global variables, long-lived timers, or event listeners.
let num = 42; // primitive number allocated on the stack
let str = "hello"; // string created and referenced
let obj = { a: 1 }; // object allocated on the heap
let arr = [1, 2, 3]; // array allocated on the heap
let user = { name: "Alice" }; // user refers to an object in the heap
// later...
user = null; // original object is now unreachable
// When no other references exist, the GC will eventually reclaim this memory.
const leaks = [];
function addLeak() {
// Creates a large string and keeps pushing it into the global array
leaks.push(new Array(1_000_000).join("x"));
}
setInterval(addLeak, 1000); // memory usage keeps growing over time
// Because 'leaks' is global and never cleared, nothing becomes unreachable.
let intervalId;
function startWork() {
let counter = 0;
intervalId = setInterval(() => {
counter++;
console.log("Tick:", counter);
}, 1000);
}
function stopWork() {
clearInterval(intervalId); // prevents the interval from running forever
intervalId = null; // reference cleared, eligible for GC
}
startWork();
// later, when work is done:
stopWork();
const metadata = new WeakMap();
function track(element, info) {
metadata.set(element, info);
}
function showInfo(element) {
console.log(metadata.get(element));
}
// If 'element' is removed from the DOM and has no other references,
// the entry in 'metadata' can be garbage-collected automatically.
In the memory leak example, the setInterval callback keeps pushing large strings into the leaks array every second. Because leaks is a global variable and we never clear it, all those strings stay reachable. The garbage collector cannot free them, so memory usage grows continuously.
In contrast, when we call clearInterval() and set intervalId = null, the timer stops, and the interval’s internal references can be released. If no other references exist to objects used inside the callback, they become unreachable and the GC can reclaim their memory.
Tools like browser DevTools let you record heap snapshots, inspect detached DOM nodes, and watch the memory timeline. If memory keeps going up without dropping back down after activity, it usually indicates a leak.
setInterval and setTimeout when they are no longer needed.WeakMap and WeakSet for data that should not prevent garbage collection.null when you are done with large objects and they are not needed anymore.setInterval and a growing array. Open DevTools and observe the memory graph while it runs.WeakMap by attaching metadata to DOM elements and then removing those elements. Use DevTools to confirm that memory isn’t retained.