The garbage collector (GC) in JavaScript
The garbage collector (GC) is a mechanism of the JavaScript engine (V8, for example) that automatically frees the memory occupied by objects that have become unreachable. In other words, GC removes from memory everything that nothing points at any more, so that resources are not wasted and memory does not overflow.
Theory
TL;DR
- GC frees memory automatically; there is no manual
free()in JavaScript. - There is exactly one criterion: reachability, not the "logical need" for an object.
- The roots of reachability: the global object, the call stack, live closures.
- The base algorithm is mark-and-sweep, sometimes with a compact phase that defragments memory.
- V8 has two modes: Minor GC (Scavenge) for young objects and Major GC (Mark-Sweep-Compact) for old ones.
- Circular references do not bother GC: all that matters is whether the object can be reached from a root.
- A leak is not a broken GC, it is a live reference to something you no longer need.
Quick example
The engine manages memory like this:
-
It allocates memory for a new object:
javascriptlet user = { name: 'Tim' }; // memory allocated -
The variable stops pointing at the object:
javascriptuser = null; // the old reference is gone -
GC sees that the object
{ name: 'Tim' }is unreachable and frees the memory on its next cycle.
The key concept: reachability
In JS memory is not cleared by hand, it is cleared by the principle of reachability.
Reachable objects:
- global variables (
window,global,globalThis); - local variables on the stack, while the function is running;
- objects referenced by other reachable objects.
Unreachable objects:
- objects with no references from any reachable place.
let user = { name: 'Alice' };
let admin = user; // two references to one object
user = null; // still reachable through admin
admin = null; // now unreachable, GC will remove itAn example with a reference chain and a cycle
function createUser() {
const user = { name: 'Bob' };
const address = { city: 'Paris' };
user.addr = address;
address.owner = user; // circular reference
return user;
}
let person = createUser();Here user points at address and address points back at user. If you then write:
person = null;both objects become unreachable, because there is no longer a path to them from a root, and GC frees both of them despite the cycle. The JS collector handles such cycles: it does not count references, it checks reachability from the roots.
The algorithm, using V8 as the example
V8 (the engine of Chrome and Node.js) uses the mark-and-sweep algorithm:
- Mark. GC walks from the root objects (
window,global, the call stack) and marks everything reachable. - Sweep. Everything that was not marked is removed from memory.
- Compact. Sometimes memory is compacted to remove fragmentation and free contiguous blocks.
On top of that, V8 has two kinds of cycle:
- Minor GC (Scavenge) for "young" objects: new ones, which usually die quickly. It runs often and fast.
- Major GC (Mark-Sweep-Compact) for "old" objects: the ones that survived several Minor GCs. It runs less often and takes longer.
How GC affects performance
- GC runs automatically and mostly asynchronously, but it sometimes causes small pauses (GC pauses).
- The more "live" objects there are, the longer a collection cycle takes.
- Memory leaks get in the way of GC: it treats dangling references as live and cannot free anything.
Optimal code means fewer long-lived objects plus dropping references at the right time.
What GC does not do
| Does not do | Why |
|---|---|
| Does not clear everything at once | So that it does not block the running program |
| Does not see "logical uselessness" | It only looks at the absence of references, it is not intelligence |
| Cannot be driven manually | There is no free() as in C++; the delete operator in JS removes a property, not memory |
| Does not clean up global objects | They are always reachable through window or globalThis |
How to inspect GC and memory
In the browser:
- Chrome DevTools, the Memory tab, "Heap snapshot";
- the Performance tab, the Memory section, the "Record" button;
- the console:
performance.memory.usedJSHeapSize.
In Node.js:
- the
--inspectflag, theheapdumpandclinic.jspackages, theprocess.memoryUsage()method.
Summary table:
| What | Description |
|---|---|
| GC (Garbage Collector) | The mechanism that frees memory automatically |
| Principle | Removes objects that cannot be reached from a root |
| Main algorithm | Mark-and-sweep |
| The leak problem | The object is still reachable but is no longer needed |
| The fix | Drop references, clear timers and listeners |
| Pros | Simplicity, memory safety |
| Cons | Possible pauses and hidden leaks |
Common mistakes
| Mistake | What happens |
|---|---|
| Global variables | They stay reachable forever, GC will never collect them |
| Timers and listeners without cleanup | The references keep closures alive together with everything inside them |
| DOM references after the element is removed | The node is out of the document but a ref holds it, so GC is powerless |
| Closures over large objects | Leaks through scope: one tiny function keeps megabytes alive |
Two more things are worth remembering. First, "the object is no longer needed" and "the object is unreachable" are different statements, and GC understands only the second one. Second, there is no way to force a reliable GC run from application code, so every optimisation comes down to releasing references in time rather than asking the collector to do some work.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.