One of the most common senior frontend interview questions is:
What is a memory leak in JavaScript?
Interviewers often ask:
- What causes memory leaks?
- How does JavaScript free memory?
- Can closures cause memory leaks?
- How do event listeners leak memory?
- How can memory leaks affect performance?
- How do memory leaks occur in React applications?
Understanding memory leaks is important because they can slowly degrade application performance and eventually crash a browser tab.
1. What is a Memory Leak?#
A memory leak occurs when memory that is no longer needed cannot be released.
Example:
Object No Longer Needed
↓
Still Referenced
↓
Cannot Be Garbage Collected
↓
Memory Leak
Over time:
More Memory Used
↓
Slower Application
↓
Potential Browser Crash
2. Memory Management & Garbage Collection#
JavaScript automatically manages memory.
Unlike languages such as C or C++:
delete memory();
free(memory);
does not exist.
JavaScript relies on:
Garbage Collection (GC)
to reclaim unused memory.
What is Garbage Collection?#
Garbage Collection is the process of automatically removing objects that are no longer reachable.
Example:
let user = {
name: "John",
};
user = null;
Now:
No References Exist
The object becomes eligible for garbage collection.
Reachability#
Garbage collectors determine whether an object is:
Reachable
or
Unreachable
Reachable objects remain in memory.
Unreachable objects can be removed.
Example#
let user = {
name: "John",
};
Reference:
Global Scope
↓
user
Object remains reachable.
Remove Reference#
let user = {
name: "John",
};
user = null;
Reference removed:
Global Scope
↓
null
The original object becomes unreachable.
3. Anatomy of a Memory Leak#
let users = [];
function addUser() {
users.push({
name: "John",
});
}
If:
addUser();
is called repeatedly:
Array Grows Forever
Memory usage continues increasing.
4. Common Causes of Memory Leaks#
The most common causes are:
- Global Variables
- Closures
- Event Listeners
- Timers
- Detached DOM Elements
- Caches
- React Effects
Let's examine each one.
1. Global Variables#
Global variables remain alive for the lifetime of the page.
Example:
var cache = [];
Adding data repeatedly:
cache.push(bigObject);
without cleanup causes memory growth.
Why Is This a Problem?#
Global scope remains reachable until:
Page Refresh
Therefore:
Nothing Can Be Collected
2. Closures#
Closures preserve references to outer variables.
Example:
function createHandler() {
const hugeData = new Array(1000000).fill("data");
return function () {
console.log(hugeData.length);
};
}
Why Is This a Leak?#
As long as:
handler;
exists:
hugeData;
cannot be garbage collected.
Visual Representation#
handler
↓
Closure
↓
hugeData
Reference remains alive.
Important Interview Point#
Closures are not memory leaks.
However:
Unnecessary Closure References
can create memory leaks.
3. Event Listener Leaks#
One of the most common frontend leaks.
Example:
const button = document.getElementById("submit");
button.addEventListener("click", handleClick);
Problem#
Suppose:
button.remove();
The DOM node is removed.
But:
handleClick;
still exists as a listener.
The browser may keep references alive.
Solution#
Always clean up:
button.removeEventListener("click", handleClick);
4. Timer Leaks#
Example:
setInterval(() => {
fetchData();
}, 1000);
Problem#
The interval continues forever.
Even if:
Component Removed
or
Feature No Longer Needed
the timer keeps running.
Solution#
const id = setInterval(fetchData, 1000);
clearInterval(id);
5. Detached DOM Elements#
A common browser memory leak.
Example:
const element = document.getElementById("card");
document.body.removeChild(element);
DOM node removed.
However:
console.log(element);
still references it.
Result#
DOM Node Exists In Memory
even though it is no longer visible.
Visual Representation#
JavaScript Variable
↓
Detached DOM Node
The browser cannot reclaim it.
6. Unbounded Caches#
Example:
const cache = {};
Store data:
cache[id] = response;
If:
Entries Never Removed
memory grows indefinitely.
Real Example#
cache["1"] = data;
cache["2"] = data;
cache["3"] = data;
...
After months of usage:
Huge Memory Consumption
Solution#
Use:
- Cache limits
- LRU caches
- Expiration strategies
7. React Memory Leaks#
A very common interview topic.
Example:
useEffect(() => {
const id = setInterval(() => {
console.log("Running");
}, 1000);
}, []);
Problem#
Component unmounts.
Interval continues.
Memory leak occurs.
Solution#
useEffect(() => {
const id = setInterval(() => {
console.log("Running");
}, 1000);
return () => {
clearInterval(id);
};
}, []);
Always clean up effects.
React Event Listener Leak#
Bad:
useEffect(() => {
window.addEventListener("resize", handleResize);
}, []);
Problem#
Listener survives component removal.
Correct#
useEffect(() => {
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);
React Fetch Request Leak#
Example:
useEffect(() => {
fetch("/api/users").then(setUsers);
}, []);
Component unmounts before response arrives.
Potential issue:
setUsers(...)
runs on an unmounted component.
Better#
useEffect(() => {
const controller = new AbortController();
fetch("/api/users", {
signal: controller.signal,
});
return () => {
controller.abort();
};
}, []);
Interview Question#
Can Closures Cause Memory Leaks?
Answer:
Yes and No
Closures themselves are not leaks.
However:
Closures Keep References Alive
If large objects remain referenced unnecessarily:
Memory Cannot Be Freed
Interview Question#
Can Garbage Collection Prevent All Memory Leaks?
No.
Garbage collection only removes:
Unreachable Objects
If references still exist:
Object Remains Reachable
and cannot be collected.
6. How to Detect Memory Leaks#
Modern browsers provide:
Chrome DevTools
Memory tools.
Common approaches:
- Heap snapshots
- Allocation timelines
- Performance profiling
Warning Signs#
Symptoms include:
- Increasing memory usage
- Slower UI
- Delayed interactions
- Browser tab crashes
- Frequent garbage collection
7. Common Interview Q&A#
What is a Memory Leak?#
Memory that is no longer needed but cannot be reclaimed because references still exist.
How Does Garbage Collection Work?#
Garbage collectors remove unreachable objects.
Reachable objects remain in memory.
Are Closures Memory Leaks?#
No.
Closures are normal JavaScript behavior.
Leaks occur when closures unnecessarily retain large objects.
Why Are Event Listeners Dangerous?#
Forgotten listeners can keep DOM nodes and objects alive.
Why Are Timers Common Leak Sources?#
Intervals and timeouts may continue running after they are no longer needed.
What Causes Most React Memory Leaks?#
Typically:
- Uncleaned intervals
- Unremoved event listeners
- Uncancelled requests
- Long-lived subscriptions
Senior-Level Interview Answer#
A memory leak occurs when memory that is no longer needed remains reachable and therefore cannot be reclaimed by JavaScript's garbage collector. Since JavaScript uses automatic memory management, developers do not manually free memory, but they are responsible for removing unnecessary references. Common sources of leaks include global variables, closures retaining large objects, event listeners, timers, detached DOM nodes, unbounded caches, and improperly cleaned React effects. Garbage collection can only remove unreachable objects, so memory leaks occur when references remain alive longer than intended.
Common Interview Mistakes#
❌ "JavaScript cannot have memory leaks because it has garbage collection."
Correct:
Garbage collection removes only
unreachable objects.
❌ "Closures are memory leaks."
Correct:
Closures are normal behavior.
Leaks occur from retained references.
❌ "Removing a DOM node frees memory."
Correct:
Only if no references remain.
❌ "React automatically prevents memory leaks."
Correct:
Developers must clean up effects,
listeners, and timers.
Memory Leak Summary#
| Source | Can Cause Leak? |
|---|---|
| Global Variables | ✅ Yes |
| Closures | ✅ Sometimes |
| Event Listeners | ✅ Yes |
| setInterval | ✅ Yes |
| Detached DOM Nodes | ✅ Yes |
| Infinite Cache Growth | ✅ Yes |
| React Effects | ✅ Yes |
Key Takeaways#
- Memory Leaks: Occur when unused memory references are not garbage collected because references remain accessible.
- Common Sources: Caused by accidental global variables, unresolved timers (
setInterval), and unremoved DOM event listeners. - Detached DOM: Nodes removed from the page layout but still referenced inside JavaScript variables.
- React Prevention: Always clean up timeouts, event listeners, and API subscriptions inside
useEffectcleanup callbacks.