Garbage Collection in JavaScript (V8) Explained
V8 uses generational mark-and-sweep. Here is how the Orinoco collector works and how to help it.
Garbage Collection in JavaScript (V8) Explained
V8's garbage collector (Orinico) automatically reclaims memory that is no longer reachable. Understanding it helps you avoid memory leaks and write efficient code.
How GC Works: Mark and Sweep
- Mark: starting from the roots (global object, call stack variables, closures), the GC marks all reachable objects.
- Sweep: unreachable objects are freed.
- Compact: surviving objects are moved together to reduce fragmentation (in the old generation).
Generational Hypothesis
V8 uses generational GC: objects are divided by age.
- Young generation: new objects. Small, collected frequently (scavenge).
- Old generation: objects that survived multiple young-gen collections. Larger, collected less frequently (mark-sweep).
The hypothesis: most objects die young (temp variables, intermediate results). So collecting the young generation frequently is cheap and effective.
Young Generation (Scavenge)
Uses Cheney's algorithm:
- Heap is split into two semi-spaces (from and to).
- Living objects in
fromare copied toto(and compacted). fromandtoswap.- Dead objects are never copied; they are just abandoned.
Fast for small heaps. Objects that survive two scavenge cycles are promoted to the old generation.
Old Generation (Mark-Sweep-Compact)
- Mark: mark all reachable objects (starting from roots).
- Sweep: free unreachable objects.
- Compact: move surviving objects together to reduce fragmentation.
Slower than scavenge, but runs less frequently.
Orinoco: Incremental and Concurrent
Old-gen collection used to be a long pause (stop-the-world). Orinoco makes it incremental and concurrent:
- Incremental: marking is done in small steps interleaved with JS execution.
- Concurrent: marking and sweeping happen on a background thread.
- Parallel: multiple GC threads help with marking.
This reduces pause times from ~50ms to a few milliseconds.
What Counts as a Root?
- Global object (
globalThis). - Call stack variables (locals of running functions).
- Closures (closed-over variables).
- DOM elements (in browsers, elements that are in the document).
What Prevents GC?
- Closures: a closure keeps its lexical environment alive.
- Event listeners: a listener on a detached DOM element keeps it alive.
- Timers: a setInterval callback keeps its closed-over variables alive.
- Global variables: a variable on
globalThisis always reachable. - Caches: an unbounded cache keeps all entries alive.
How to Help the GC
- Remove event listeners when done.
- Clear timers (
clearInterval,clearTimeout). - Do not close over large objects unnecessarily.
- Use
WeakMap/WeakSetfor caches (entries are removed when the key is GC'd). - Null out references when no longer needed.
- Avoid global variables.
- Bound caches (LRU, max size).
The Takeaway
V8 uses generational mark-and-sweep (Orinoco). Young objects are collected frequently (scavenge); old objects less frequently (mark-sweep-compact). Orinoco is incremental and concurrent to reduce pauses. Closures, listeners, timers, and unbounded caches can prevent GC. Help the GC by removing listeners, clearing timers, using WeakMap, and nulling references.
V8 uses generational mark-and-sweep (Orinoco). New objects are in the young generation (collected frequently via scavenge). Surviving objects are promoted to the old generation (collected less frequently via mark-sweep-compact). The GC is incremental and concurrent to minimize pauses.
Most objects die young (temp variables, intermediate results). So collecting the young generation frequently is cheap and effective. Old objects are collected less frequently because they tend to survive longer.
V8's modern GC that uses incremental and concurrent marking and sweeping to reduce pause times. Instead of a long stop-the-world pause, marking is done in small steps interleaved with JS execution, and sweeping happens on a background thread.
Closures (keep lexical environments alive), event listeners on detached DOM elements, uncleared timers, global variables, and unbounded caches. These keep objects reachable, so the GC cannot free them.
Remove event listeners when done, clear timers (clearInterval, clearTimeout), do not close over large objects unnecessarily, use WeakMap/WeakSet for caches, null out references when no longer needed, avoid global variables, and bound cache sizes (LRU).
Ready to master React completely?
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course to dive deeper with high-quality video tutorials, solve interview questions, and a premium community.
Master React
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course.

