docs/heap/marking-and-sweeping.md
This document provides a deep dive into the implementation of the Mark-Sweep-Compact garbage collection algorithm used in V8's Old Generation (Major GC).
The Major GC in V8 follows three main phases:
V8 uses a bitmap-based marking approach combined with a worklist to achieve efficient and concurrent marking.
1, and it is not in the worklist.While background threads do most of the marking, the main thread is responsible for:
WeakCell) are processed at the end of marking on the main thread when the transitive closure is known.Defined in src/heap/marking.h, the MarkingBitmap is a sequence of cells containing bits. Each page in the heap has an associated marking bitmap.
The MarkingVisitor (defined in src/heap/marking-visitor.h) is responsible for traversing the object graph.
BodyDescriptor).To reduce pause times, V8 performs marking concurrently on background threads while the main thread continues to execute JavaScript.
MarkingWorklist uses thread-local segments for pushing and popping objects to avoid synchronization overhead.To support both generational GC and concurrent marking, V8 uses a Combined Write Barrier. Whenever a pointer is written to a heap object (object.field = value), the barrier checks:
object is in the old generation and the value is in the young generation (or shared heap), the slot is recorded in a remembered set.value is a white object (unmarked), it is atomically marked grey and pushed to the marking worklist.Concurrent marking introduces data races if a background thread reads an object while the main thread modifies it.
During the compaction phase, live objects are moved to new locations.
cppgc (V8's C++ garbage collector) for objects that cannot be traced concurrently, but in the V8 JS heap, it is referred to as the on-hold worklist.If the main thread changes an object's layout (e.g., transitioning a field from tagged to untagged) or transitions a string while a background thread is scanning it, it could cause a crash or inconsistent state.
ObjectLockGuard) to ensure exclusive access during scanning by background threads.After marking completes, all objects with a 0 in the marking bitmap are considered dead.
The Sweeper (defined in src/heap/sweeper.cc):
Filler objects (dead space) and adds them to the space's Free List.Like marking, sweeping is heavily parallelized and can run concurrently with JavaScript execution.
Over time, the Old Generation can become fragmented. Sweeping leaves free spaces, but they might be too small for large objects.
To combat this, V8 may choose to compact certain pages during a Major GC:
Compaction is expensive because it involves copying objects and updating pointers, so V8 only performs it on heavily fragmented pages.