Skip to main content

The garbage collector (GC) in JavaScript

The garbage collector (GC) is a mechanism of the JavaScript engine (V8, for example) that automatically frees the memory occupied by objects that have become unreachable. In other words, GC removes from memory everything that nothing points at any more, so that resources are not wasted and memory does not overflow.

Theory

TL;DR

  • GC frees memory automatically; there is no manual free() in JavaScript.
  • There is exactly one criterion: reachability, not the "logical need" for an object.
  • The roots of reachability: the global object, the call stack, live closures.
  • The base algorithm is mark-and-sweep, sometimes with a compact phase that defragments memory.
  • V8 has two modes: Minor GC (Scavenge) for young objects and Major GC (Mark-Sweep-Compact) for old ones.
  • Circular references do not bother GC: all that matters is whether the object can be reached from a root.
  • A leak is not a broken GC, it is a live reference to something you no longer need.

Quick example

The engine manages memory like this:

  1. It allocates memory for a new object:

    javascript
    let user = { name: 'Tim' }; // memory allocated
  2. The variable stops pointing at the object:

    javascript
    user = null; // the old reference is gone
  3. GC sees that the object { name: 'Tim' } is unreachable and frees the memory on its next cycle.

The key concept: reachability

In JS memory is not cleared by hand, it is cleared by the principle of reachability.

Reachable objects:

  • global variables (window, global, globalThis);
  • local variables on the stack, while the function is running;
  • objects referenced by other reachable objects.

Unreachable objects:

  • objects with no references from any reachable place.
javascript
let user = { name: 'Alice' }; let admin = user; // two references to one object user = null; // still reachable through admin admin = null; // now unreachable, GC will remove it

An example with a reference chain and a cycle

javascript
function createUser() { const user = { name: 'Bob' }; const address = { city: 'Paris' }; user.addr = address; address.owner = user; // circular reference return user; } let person = createUser();

Here user points at address and address points back at user. If you then write:

javascript
person = null;

both objects become unreachable, because there is no longer a path to them from a root, and GC frees both of them despite the cycle. The JS collector handles such cycles: it does not count references, it checks reachability from the roots.

The algorithm, using V8 as the example

V8 (the engine of Chrome and Node.js) uses the mark-and-sweep algorithm:

  1. Mark. GC walks from the root objects (window, global, the call stack) and marks everything reachable.
  2. Sweep. Everything that was not marked is removed from memory.
  3. Compact. Sometimes memory is compacted to remove fragmentation and free contiguous blocks.

On top of that, V8 has two kinds of cycle:

  • Minor GC (Scavenge) for "young" objects: new ones, which usually die quickly. It runs often and fast.
  • Major GC (Mark-Sweep-Compact) for "old" objects: the ones that survived several Minor GCs. It runs less often and takes longer.

How GC affects performance

  • GC runs automatically and mostly asynchronously, but it sometimes causes small pauses (GC pauses).
  • The more "live" objects there are, the longer a collection cycle takes.
  • Memory leaks get in the way of GC: it treats dangling references as live and cannot free anything.

Optimal code means fewer long-lived objects plus dropping references at the right time.

What GC does not do

Does not doWhy
Does not clear everything at onceSo that it does not block the running program
Does not see "logical uselessness"It only looks at the absence of references, it is not intelligence
Cannot be driven manuallyThere is no free() as in C++; the delete operator in JS removes a property, not memory
Does not clean up global objectsThey are always reachable through window or globalThis

How to inspect GC and memory

In the browser:

  • Chrome DevTools, the Memory tab, "Heap snapshot";
  • the Performance tab, the Memory section, the "Record" button;
  • the console: performance.memory.usedJSHeapSize.

In Node.js:

  • the --inspect flag, the heapdump and clinic.js packages, the process.memoryUsage() method.

Summary table:

WhatDescription
GC (Garbage Collector)The mechanism that frees memory automatically
PrincipleRemoves objects that cannot be reached from a root
Main algorithmMark-and-sweep
The leak problemThe object is still reachable but is no longer needed
The fixDrop references, clear timers and listeners
ProsSimplicity, memory safety
ConsPossible pauses and hidden leaks

Common mistakes

MistakeWhat happens
Global variablesThey stay reachable forever, GC will never collect them
Timers and listeners without cleanupThe references keep closures alive together with everything inside them
DOM references after the element is removedThe node is out of the document but a ref holds it, so GC is powerless
Closures over large objectsLeaks through scope: one tiny function keeps megabytes alive

Two more things are worth remembering. First, "the object is no longer needed" and "the object is unreachable" are different statements, and GC understands only the second one. Second, there is no way to force a reliable GC run from application code, so every optimisation comes down to releasing references in time rather than asking the collector to do some work.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.