DevAcademy
LearnJavaScriptMemory Management & Garbage Collection
AdvancedJavaScript

Memory Management & Garbage Collection

Learn how JavaScript allocates and releases memory automatically, how the mark-and-sweep garbage collector decides what to free, common memory leak patterns, and how WeakMap/WeakSet help avoid them.

Reading Time

18 min

Lesson

Lesson 48 of 48

The Memory Lifecycle

Every value your program uses goes through the same three-step memory lifecycle: allocate the memory it needs, use that memory (reading and writing values), and eventually release it once it's no longer needed. In languages like C, the programmer is responsible for all three steps by hand, including explicitly calling something like free(). JavaScript automates the last step: you never call free() or delete on a value's underlying memory — instead, a background process called the garbage collector figures out when memory is safe to reclaim and does it for you.

Allocation Happens Constantly, Automatically

You allocate memory constantly without thinking about it: declaring a variable, creating an object literal or array, defining a function, all reserve memory behind the scenes. There's no explicit "allocate" step you write yourself — JavaScript's engine handles it the moment a value is created. The part worth understanding deeply is the release step, because that's the part JavaScript automates on your behalf, and automation only works well if you understand what it's doing.

Reachability: The Core Idea

Garbage collectors work on the concept of reachability. A set of values are always considered reachable and are never collected — these are the roots, which include the global object, and any variables currently on the call stack in functions that haven't returned yet. Any value reachable from a root, by following references (object properties, array elements, closures) however many steps deep, is also considered reachable and kept alive. A value becomes eligible for garbage collection only when it is no longer reachable from any root — that is, nothing in the program can get to it anymore.

Mark-and-Sweep

The algorithm modern JavaScript engines use is called mark-and-sweep. Periodically, the garbage collector starts at the roots and walks every reference it can find, marking every value it reaches along the way as "in use." Once that walk is finished, it sweeps through memory and reclaims everything that was not marked — those are, by definition, the values nothing in the program can reach anymore. This is a meaningful improvement over older approaches like reference counting, because mark-and-sweep correctly handles circular references (two objects that reference each other but that nothing else references) — since neither is reachable from a root, both get collected, whereas naive reference counting would keep them alive forever.

Reachability in Practice

Try it yourself — edit and run

Console Output

Click “Run” to see the console output here.

Common Memory Leak Patterns

  • Accidental globals — forgetting a variable declaration (e.g. `total = 0` instead of `let total = 0`) attaches the variable to the global object, so it lives for the entire lifetime of the program and is never collected.
  • Forgotten timers and intervals — a setInterval() that's never cleared with clearInterval() keeps running forever, and keeps every variable its callback closes over reachable indefinitely.
  • Detached DOM nodes — removing an element from the page with something like element.remove() doesn't free its memory if a JavaScript variable elsewhere still references it; the node is detached from the visible page but still fully reachable, and the garbage collector will not touch it.
  • Closures retaining large scopes — a closure keeps its entire outer scope alive for as long as the closure itself is reachable, so if that outer scope happens to hold a large array or object that is no longer needed, the closure silently keeps it around.
  • Event listeners never removed — attaching a listener to a long-lived object (like window) with a callback that closes over other data keeps that data reachable for as long as the listener is attached, even after the relevant UI is gone.

A Memory Leak: Forgotten Interval

Try it yourself — edit and run

Console Output

Click “Run” to see the console output here.

The Fix: Clear the Interval When Done

Try it yourself — edit and run

Console Output

Click “Run” to see the console output here.

WeakMap and WeakSet

The Map and Set objects from the earlier lesson hold strong references to everything stored in them — as long as a Map exists and has a key in it, that key (even if it's an object) stays reachable and cannot be garbage collected, no matter what else in the program still references it. WeakMap and WeakSet solve this by holding weak references to their keys (WeakMap) or values (WeakSet): if nothing other than the WeakMap or WeakSet references an object, the garbage collector is free to collect it, and the entry is automatically removed. This makes them a good fit for attaching extra data to an object — like caching or metadata — without accidentally keeping that object alive forever just because it's in your map.

WeakMap vs. Map

Try it yourself — edit and run

Console Output

Click “Run” to see the console output here.

Map/Set vs. WeakMap/WeakSet

FeatureMap / SetWeakMap / WeakSet
Reference strengthStrong — keeps keys/values aliveWeak — does not prevent garbage collection
Keys allowedAny valueObjects only (no primitives)
Iterable / has sizeYesNo — cannot be iterated or measured
Typical useGeneral-purpose storageAttaching metadata to objects you don’t own the lifetime of

Why WeakMap and WeakSet Cannot Be Iterated

Because entries can disappear at any moment as the garbage collector runs, WeakMap and WeakSet deliberately have no size property, no keys()/values()/entries(), and are not iterable — allowing iteration would expose the unpredictable timing of garbage collection to your code, which the spec avoids entirely.

When to Reach for a WeakMap

A good rule of thumb: if you find yourself attaching auxiliary data to an object (like caching a computed result keyed by that object, or storing private data associated with a class instance) and you don't want that association to prevent the object from being garbage collected once the rest of the program is done with it, use a WeakMap instead of a Map.

Detached DOM Nodes Are a Silent Leak

Removing an element from the page does not make it eligible for garbage collection if a variable, array, or closure elsewhere in your code still holds a reference to it. These "detached" nodes are invisible on the page but still fully alive in memory, and they're one of the most common sources of memory leaks in long-running single-page applications — always clean up references to removed elements alongside removing them from the DOM.

Interview Questions

Quick Quiz

1. Which step of the memory lifecycle does JavaScript handle automatically?

2. What makes a value eligible for garbage collection?

3. Why does mark-and-sweep correctly handle circular references?

4. Why can a detached DOM node still cause a memory leak?

5. What is a key difference between WeakMap and Map?

Previous