How V8 Garbage Collector Works in JavaScript
V8 uses a generational garbage collector to manage memory automatically. Learn how the Scavenger and Mark-Sweep collectors work together to clean up unused objects.
V8 manages memory automatically through garbage collection. You never call free or delete in JavaScript. The engine tracks which objects are reachable and reclaims memory from those that are not.
V8 uses a generational garbage collector. It splits the heap into two generations because most objects die young.
Temporary strings, intermediate calculation results, and short-lived closures are created and discarded quickly. Objects that survive for a while tend to survive much longer. The generational approach collects young objects frequently and old objects less often.
The Two Generations
V8's heap has two main regions: the young generation and the old generation.
The young generation, also called the nursery, is where new objects are allocated. It is small, typically a few megabytes, and collected frequently. The collection is fast because the nursery is small and most objects in it are already dead.
The old generation holds objects that have survived one or more nursery collections. It is much larger, potentially gigabytes, and collected less frequently. Objects here have proven they live long enough to be worth keeping.
New objects start in the nursery. If they survive a nursery collection, they are promoted (moved) to the old generation. After a few survivals, they become old generation permanent residents.
The Scavenger: Minor GC
The nursery is collected by the Scavenger, also called minor GC. It uses a copying collector based on Cheney's algorithm.
The nursery is split into two equal halves: the from-space and the to-space. Allocation happens in the from-space. When it fills up, the Scavenger runs.
It identifies live objects in the from-space, copies them to the to-space, and updates all pointers to point to the new locations. Dead objects are simply left behind. When copying is done, the spaces swap roles.
The Scavenger is stop-the-world: JavaScript execution pauses during collection. But the pause is short because the nursery is small, typically a few milliseconds.
Surviving objects that have been copied once or twice are promoted to the old generation rather than staying in the nursery. This keeps the nursery small and fast to collect.
The Mark-Sweep and Mark-Compact: Major GC
The old generation is collected by the Mark-Sweep and Mark-Compact collectors, collectively called major GC. This happens much less frequently than minor GC because the old generation is large and collection is more expensive.
Mark-Sweep works in two phases. The mark phase starts from roots (global objects, stack variables, active closures) and traces all reachable objects. Every reachable object is marked as live.
The sweep phase walks the entire old generation and reclaims memory from unmarked objects, adding it back to the free list.
Mark-Compact adds a third phase: after marking, live objects are moved together to reduce fragmentation. This is more expensive than Mark-Sweep but prevents the old generation from becoming a checkerboard of small free chunks.
Concurrent and Incremental Marking
Stop-the-world collection pauses JavaScript. For the old generation, these pauses could last hundreds of milliseconds, which is unacceptable for smooth web pages. V8 uses concurrent and incremental techniques to reduce pause times.
Concurrent marking runs the mark phase in a background thread while JavaScript continues executing. The main thread is only paused briefly at the start and end of marking. This nearly eliminates the mark phase from the pause time.
Incremental marking splits the mark phase into small steps interleaved with JavaScript execution. The main thread marks a few objects, runs some JavaScript, marks a few more, and so on. Each step is a fraction of a millisecond, keeping the page responsive.
What Triggers Garbage Collection
Garbage collection is triggered when allocation fails. When the nursery is full and you try to create a new object, the Scavenger runs to free up space. When the old generation cannot satisfy an allocation request, major GC runs.
The engine also triggers GC heuristically. If the system has plenty of free memory, V8 delays collection to avoid unnecessary pauses. If memory is tight, V8 collects more aggressively.
On mobile devices with limited memory, V8 uses a smaller heap and collects more often.
Embedders like Chrome and Node.js can also trigger GC at convenient moments, such as when a page is idle or when navigating to a new page.
How to Reduce GC Pressure
You cannot control the garbage collector directly, but you can write code that minimizes its workload. Less garbage means fewer and shorter GC pauses.
Reuse objects instead of creating new ones in hot loops. Every new object allocation eventually needs collection. If a loop runs thousands of times per second, allocating a new object on each iteration creates significant GC pressure.
Avoid large temporary data structures. A function that builds a large array, processes it, and discards it creates a spike of garbage. Consider processing data incrementally or using typed arrays for numeric data, which are allocated outside the regular object heap.
Clean up event listeners and timer references when they are no longer needed. An active setTimeout that references a large object keeps that object alive indefinitely. For more on preventing memory leaks, see /javascript/fixing-javascript-memory-leaks-complete-guide.
Weak References and Finalization
V8 supports WeakRef and FinalizationRegistry, introduced in ES2021. A WeakRef holds a reference to an object without preventing garbage collection. If the object is collected, the WeakRef returns undefined.
FinalizationRegistry lets you register a callback that runs after an object is garbage collected. This is useful for cleaning up native resources like file handles or WebGL contexts.
These features should be used sparingly. Garbage collection timing is unpredictable.
A WeakRef may return the object on one access and undefined on the next if GC happened in between. Finalization callbacks may run much later than expected or not at all if the program ends first.
GC Across JavaScript Engines
All JavaScript engines use generational garbage collection, but the implementations differ. SpiderMonkey uses a generational collector with a copying nursery and a mark-and-sweep old generation. JavaScriptCore uses a similar design with additional optimization for concurrent marking.
The performance characteristics are similar across engines. The nursery is always fast to collect. The old generation is always slower.
Minimizing allocations in hot code helps every engine.
For more on how V8 manages objects internally, see /javascript/v8-hidden-classes-in-javascript-full-tutorial.
Rune AI
Key Insights
- V8 uses a generational garbage collector with two generations: young (nursery) and old.
- The Scavenger uses a fast copying collector for the nursery, promoting survivors to the old generation.
- The old generation uses Mark-Sweep and Mark-Compact to reclaim memory from long-lived objects.
- V8 employs concurrent and incremental techniques to reduce pause times.
- Avoiding unnecessary object creation and cleaning up references helps the GC work efficiently.
Frequently Asked Questions
Does V8's garbage collector run on a separate thread?
Can I manually trigger garbage collection?
Conclusion
V8's garbage collector is a carefully engineered system that balances speed and memory. The Scavenger quickly reclaims short-lived objects. The Mark-Sweep collector periodically cleans the old generation. Understanding how these collectors work helps you write code that minimizes GC pressure and avoids memory leaks.
More in this topic
Using Decorators for Logging in JS Architecture
Learn how JavaScript decorators wrap class methods to add logging without touching the original code, including setup with Babel and how execution order works.
JavaScript While Loop Explained: A Complete Guide
A while loop repeats code as long as a condition stays true. Learn the syntax, see practical examples, and understand when a while loop is the right choice over a for loop.
JavaScript Loops Tutorial: for, while, do while
Loops let JavaScript repeat code without writing it twice. Learn the three core loop types, what each one does, and when to choose one over another.