Unlike some languages, JavaScript never requires manually freeing memory — a garbage collector does it automatically. Understanding roughly how it decides what to free explains most real-world memory leaks.
| Step | What happens |
|---|---|
| Allocate | Memory is reserved when a value is created — a variable, an object, a function |
| Use | The program reads and writes to that memory |
| Release | The garbage collector frees the memory once nothing can reach it anymore |
An object is kept in memory as long as it's reachable — reachable directly or indirectly from a "root" (global variables, the current call stack). The instant nothing references an object anymore, it becomes eligible for collection.
let user = { name: 'Sam' } // the object is reachable via 'user'
user = null // nothing references the object anymore —
// eligible for garbage collectionA running setInterval keeps everything its callback references reachable, indefinitely, until explicitly cleared — even if the code that created it is long gone.
// LEAK — this interval, and everything it closes over, never gets freed
function startPolling() {
const data = fetchLargeDataset()
setInterval(() => {
console.log(data.length)
}, 1000)
}
// FIXED — store the interval ID and clear it when it's no longer needed
function startPolling() {
const data = fetchLargeDataset()
const intervalId = setInterval(() => {
console.log(data.length)
}, 1000)
return () => clearInterval(intervalId)
}A removed DOM element still can't be garbage collected if a variable somewhere still references it.
let cachedButton = document.querySelector('#submit')
cachedButton.remove() // removed from the page, but NOT freed —
// cachedButton still references it
cachedButton = null // now it can actually be collectedelement.addEventListener('click', handleClick)
// If element is removed from the DOM without also calling:
element.removeEventListener('click', handleClick)
// the listener (and anything handleClick closes over) can stay in memoryChecking for a leak in practice
The Memory tab in browser DevTools can take a heap snapshot and compare it over time — a growing number of detached DOM nodes between snapshots is the clearest practical sign of a real leak.