How JavaScript Garbage Collection Works

JavaScript automatically manages memory allocation and release through an internal process known as garbage collection. This article explores the JavaScript memory life cycle, breaks down the core algorithms used to identify unreachable objects—specifically reference counting and mark-and-sweep—and highlights common memory leak patterns developers should avoid to maintain optimal application performance.

The JavaScript Memory Life Cycle

Memory management across all programming languages follows a three-step cycle:

  1. Allocation: Memory is reserved by the operating system when values, objects, or functions are declared.
  2. Usage: The allocated memory is read or modified within your application logic.
  3. Release: Memory that is no longer needed is freed up for future allocations.

In low-level languages like C, developers manually allocate and deallocate memory. In JavaScript, memory allocation happens automatically when variables are initialized, and deallocation is handled automatically in the background by the Garbage Collector (GC).

Core Garbage Collection Algorithms

The primary challenge in automated memory management is determining when allocated memory is no longer needed. JavaScript engines utilize specific algorithms to solve this problem.

1. Reference-Counting Garbage Collection

This is a naive garbage collection algorithm. It operates on the principle of object references:

The Limitation (Circular References): Reference counting fails when two objects reference each other, creating a cycle. Even if both objects are disconnected from the rest of the application, their reference counts never drop to zero, preventing the engine from reclaiming that memory.

function createCycle() {
  const objA = {};
  const objB = {};

  objA.other = objB; // objA references objB
  objB.other = objA; // objB references objA

  // Both variables fall out of scope, but reference count remains > 0
}
createCycle();

2. Mark-and-Sweep Algorithm

Modern JavaScript engines (such as V8 in Chrome and Node.js, SpiderMonkey in Firefox, and JavaScriptCore in Safari) use the Mark-and-Sweep algorithm, which solves the circular reference problem by focusing on reachability rather than reference counts.

The algorithm operates in two primary phases:

  1. Mark Phase: The engine defines a set of “roots” (such as the global object, current local variables, and call stack parameters). The garbage collector traverses the object graph starting from these roots, marking every object it can reach as “alive.” Any object referenced by a reachable object is also marked.
  2. Sweep Phase: The engine scans the unallocated memory space and reclaims all memory associated with objects that were not marked during the traversal.

Because unreachable cycles cannot be reached from the root objects, the mark-and-sweep algorithm safely identifies and frees circular references.

Engine Optimizations (V8 Implementation)

Modern engines implement advanced strategies to prevent garbage collection from pausing execution and degrading user experience:

Common Memory Leaks in JavaScript

Even with automatic garbage collection, memory leaks occur when references to unused objects are unintentionally retained:

Writing Memory-Efficient JavaScript

To assist the garbage collector and prevent performance bottlenecks: