Skip to main content

Overview

Garbage collection (GC) is the automatic memory management system that JavaScript engines use to reclaim memory occupied by objects that are no longer reachable. Understanding GC mechanics is essential for writing performant JavaScript applications and diagnosing memory issues.
Modern JavaScript engines use sophisticated GC strategies to minimize pause times while efficiently reclaiming memory. The techniques covered here are implemented in production engines like V8, SpiderMonkey, and JavaScriptCore.

Mark-and-sweep garbage collection

Mark-and-sweep is the fundamental algorithm underlying most JavaScript garbage collectors. It operates in two distinct phases:
1

Mark phase

The collector starts from root objects (global variables, stack references, active closures) and traverses the object graph, marking every reachable object.
2

Sweep phase

The collector scans through the heap, reclaiming memory from any unmarked objects. These objects are unreachable and safe to collect.

How marking works

The marking phase uses graph traversal to identify live objects:

Sweep implementation

After marking completes, the sweep phase reclaims unmarked memory:
Mark-and-sweep requires pausing JavaScript execution during collection. This “stop-the-world” behavior can cause noticeable latency in applications, which is why modern engines use incremental and concurrent techniques.

Generational collection

Generational GC exploits the weak generational hypothesis: most objects die young. By dividing the heap into generations, engines can collect short-lived objects more frequently and efficiently.

Young generation (nursery)

New objects are allocated in the young generation, which is small and collected frequently.Characteristics:
  • Fast allocation (bump-pointer allocator)
  • Frequent collection (minor GC)
  • Low pause times (small heap region)
  • High mortality rate (90%+ objects die young)
Collection process:
1

Scavenge

Copy live objects to a survivor space using Cheney’s algorithm
2

Promotion

Objects that survive multiple collections are promoted to old generation
3

Reclaim

Entire young generation space is reclaimed and reset

Incremental and concurrent GC

To reduce pause times, modern engines break GC work into smaller increments and perform collection concurrently with JavaScript execution.

Incremental marking

Instead of marking the entire heap at once, incremental GC interleaves small marking steps with JavaScript execution.
1

Start marking

Begin marking from roots in a small time slice (1-5ms)
2

Resume execution

Let JavaScript run while maintaining marking progress
3

Continue marking

Resume marking in subsequent time slices
4

Finish marking

Complete marking when the object graph is fully traversed
Incremental GC requires tri-color marking (white/gray/black) and write barriers to track objects modified during JavaScript execution between marking increments.

Concurrent marking

Concurrent GC performs marking on separate threads while JavaScript continues executing on the main thread. Benefits:
  • Main thread pause times reduced to microseconds
  • Marking throughput increased with parallelism
  • Better utilization of multi-core CPUs
Challenges:
  • Requires thread-safe data structures
  • Needs synchronization for object graph mutations
  • More complex write barrier implementation

WeakMap and WeakSet implementation

Weak collections hold references that don’t prevent garbage collection, enabling memory-conscious caching and metadata storage patterns.

How weak references work

Weak references are invisible to the garbage collector. An object is collectible if only weak references point to it.

WeakMap use cases

Implementation details

Ephemeron tables: WeakMap/WeakSet use ephemeron tables, where key liveness determines entry liveness.
WeakMap keys must be objects. Primitives cannot be weak keys because they don’t have identity that can be collected.

Memory profiling and leak detection

Identifying and fixing memory issues requires understanding profiling tools and common leak patterns.

Using heap snapshots

1

Capture baseline

Take a heap snapshot at application start or after initialization
2

Exercise application

Perform actions that might leak memory (create/destroy components, navigate)
3

Capture comparison

Take another snapshot and compare to baseline
4

Analyze growth

Look for object types that grew unexpectedly (should have been collected)

Common memory leak patterns

Measuring GC impact

Target GC pause times: < 16ms for 60fps applications, < 100ms for perceived smoothness. Use incremental/concurrent GC to stay within budget.

Next steps

Event loop

Learn how JavaScript schedules asynchronous work with microtasks and macrotasks

JIT optimization

Explore how engines optimize hot code paths with just-in-time compilation