Mark-and-Sweep Garbage Collection in JavaScript
JavaScript manages memory automatically through an internal process called garbage collection, relying primarily on the mark-and-sweep algorithm to identify and reclaim unused memory. This algorithm operates by determining object “reachability” starting from a set of base root objects. By systematically traversing references and clearing unreferenced data, the mark-and-sweep algorithm prevents memory leaks and ensures applications maintain optimal performance without requiring manual memory deallocation from developers.
The Concept of Reachability
The foundation of the mark-and-sweep algorithm is the concept of reachability. In JavaScript, a value is considered reachable if it is accessible or usable in some way.
The algorithm defines a base set of values called “roots,” which
include: * Global variables (e.g., window in browsers,
global in Node.js) * Currently executing functions’ local
variables and parameters * Variables in the current call stack chain
Any object directly referenced by a root, or indirectly referenced through a chain of references from a root, is considered reachable. If an object cannot be reached through any path starting from the roots, it is deemed unreachable and marked for deletion.
How the Algorithm Works: The Two Phases
The mark-and-sweep process executes in two distinct phases:
1. The Mark Phase
When the garbage collector runs, it begins at the roots and traverses the entire graph of object references. * The collector visits every root and marks it as active (reachable). * It then recursively inspects all properties and references within those roots, marking every discovered object. * This process continues until all reachable nodes in the memory tree are visited and marked. * Any object that was not traversed during this step remains unmarked.
2. The Sweep Phase
Once traversal is complete, the garbage collector scans through the entire heap memory linearly: * It identifies all memory segments containing objects that lack the “marked” flag. * The memory allocated to these unmarked objects is freed and returned to the pool of available memory. * The collector clears the marks from the surviving objects in preparation for the next garbage collection cycle.
Overcoming the Circular Reference Problem
Older garbage collection strategies, such as reference counting, tracked the number of references pointing to each object. If two unneeded objects referenced each other, their reference counts never dropped to zero, creating an inescapable memory leak.
The mark-and-sweep algorithm solves this issue. Because reachability is evaluated strictly from the roots downward, isolated islands of mutually referencing objects that have no connection to the root tree are left unmarked. Consequently, the sweep phase successfully deallocates circular references.
Modern Optimizations
While standard mark-and-sweep requires pausing script execution (known as a “stop-the-world” pause), modern JavaScript engines (like V8 in Chrome and Node.js) implement several optimizations:
- Generational Collection: Memory is divided into “New Space” (short-lived objects) and “Old Space” (surviving objects). Short-lived objects are collected frequently and rapidly.
- Incremental Marking: The engine splits the marking phase into smaller intervals, interleaving them with regular JavaScript execution to prevent user interface stutter.
- Concurrent/Parallel Sweeping: The sweeping process is offloaded to background helper threads, allowing the main execution thread to continue running code uninterrupted.