Memory Management & Garbage Collection

10 questions found

How does JavaScript decide when to free memory used by an object?

Beginner
JavaScript uses automatic garbage collection: an object's memory is reclaimed once it becomes unreachable — meaning no live reference chain from the global scope, active call stack, or closures leads to it anymore. You never manually free memory like in C.
let obj = { data: 'large' };
obj = null; // no more references -> eligible for garbage collection
Real-world example Understanding why setting a variable to null can help release a large object sooner, though it's rarely required.

Common follow-ups: Does setting a variable to null immediately free the memory?

Types & Coercion

What is a memory leak in JavaScript, given that it has automatic garbage collection?

Beginner
A memory leak happens when objects remain reachable (and therefore never collected) even though the program logically no longer needs them — typically because a lingering reference (a global variable, a forgotten event listener, or a closure) accidentally keeps them alive.
function setup() {
  const bigData = new Array(1000000).fill('x');
  window.leaked = bigData; // accidental global reference keeps it alive forever
}
Real-world example Diagnosing why a single-page app's memory usage keeps climbing the longer a user stays on the page.

Common follow-ups: What browser tool would you use to find the source of a memory leak?

Debugging Testing & Tooling

How can an unremoved event listener cause a memory leak?

Intermediate
If an event listener's callback closes over a large object or a whole DOM subtree, and the listener is never removed even after the associated element is removed from the page, that closure (and everything it references) stays reachable indefinitely through the listener registration.
function attach(el) {
  const largeData = fetchLargeDataset();
  el.addEventListener('click', () => console.log(largeData.length));
  // if el is removed from DOM but listener never removed, largeData still leaks
}
Real-world example A single-page app where navigating between views repeatedly, without cleaning up listeners, gradually degrades performance.

Common follow-ups: Does removing an element from the DOM automatically remove its event listeners?

DOM & Events

How do closures unintentionally keep large objects alive longer than expected?

Intermediate
A closure retains access to its entire enclosing scope, not just the specific variables it uses — so if a large object is declared in the same scope as a function that's kept alive (e.g. returned or stored), that object can stay reachable via the closure even if the returned function never actually uses it.
function outer() {
  const largeArray = new Array(1000000).fill('data');
  const smallValue = 42;
  return function inner() {
    return smallValue; // doesn't use largeArray, but engines may still retain the whole scope
  };
}
Real-world example Investigating unexpectedly high memory usage traced back to a closure retaining an entire unused scope.

Common follow-ups: How would you restructure the code to avoid retaining largeArray unnecessarily?

Scope Hoisting & Closures

What is the difference between the generational garbage collection strategy's 'young' and 'old' generations?

Intermediate
Most objects die young (short-lived, like temporary variables in a function), so V8's generational GC checks a small 'young generation' heap frequently and cheaply; objects that survive several collections get promoted to the 'old generation', which is scanned less often since long-lived objects are less likely to become garbage soon.
// Conceptual, not code the developer controls directly:
// Young gen: fast, frequent 'Scavenge' collections
// Old gen: slower, less frequent 'Mark-Sweep-Compact' collections
Real-world example Understanding why creating many short-lived temporary objects in a hot loop is usually cheap, since the young-gen collector is optimized for exactly that pattern.

Common follow-ups: Why does frequent promotion of objects to the old generation hurt performance?

Performance Optimization: Debouncing Throttling & Memoization

How does mark-and-sweep garbage collection actually determine which objects are reachable?

Advanced
The collector starts from a set of 'roots' (global objects, currently executing call stack, active closures) and traverses every reference reachable from them, marking each visited object as live; anything left unmarked after this traversal is unreachable garbage and gets swept (freed).
// Roots -> traversal example (conceptual)
// window -> app -> currentUser -> profile (all marked reachable)
// an object with zero incoming references from any root is swept
Real-world example Explaining why two objects that only reference EACH OTHER, but nothing else references them, are still correctly collected (unlike naive reference counting).

Common follow-ups: Why does mark-and-sweep handle circular references correctly, unlike simple reference counting?

Design Patterns in JavaScript

How can a growing array used as a cache with no eviction policy become a memory leak?

Advanced
If you keep pushing entries into a cache array/object without ever removing old ones, it grows unbounded for the lifetime of the app, retaining every cached value forever even if most are never accessed again — effectively a slow, self-inflicted leak.
const cache = [];
function cacheResult(key, value) {
  cache.push({ key, value }); // never removed -> unbounded growth
}
Real-world example A long-running dashboard app whose memory usage climbs steadily because every API response ever fetched stays cached forever.

Common follow-ups: How would you bound this cache, e.g. with an LRU eviction strategy?

Map Set WeakMap & WeakSet

How would you use Chrome DevTools' heap snapshot comparison to find a leak's source?

Advanced
Take a heap snapshot, perform the suspected leaking action several times, take another snapshot, then use the 'Comparison' view to see which object types grew in count between snapshots — objects that keep increasing without ever decreasing point directly at the leak's retaining structure.
// Workflow, not code:
// 1. Take Snapshot 1
// 2. Trigger the suspected action 5x
// 3. Take Snapshot 2
// 4. Filter Comparison view by '#Delta', inspect retainers of growing objects
Real-world example Pinpointing that a 'DetachedHTMLDivElement' count keeps growing, revealing DOM nodes removed from the page but still referenced in JS.

Common follow-ups: What does a 'Detached DOM tree' in a heap snapshot specifically indicate?

Debugging Testing & Tooling

Why can setInterval() be a subtle source of memory leaks, and how do you avoid it?

Advanced
A running setInterval() keeps its callback (and everything it closes over) alive indefinitely until clearInterval() is called — if the component or context that started it is destroyed without ever clearing the interval, the callback and its retained scope leak for the rest of the page's lifetime.
function startPolling() {
  const largeState = loadInitialState();
  const id = setInterval(() => checkStatus(largeState), 1000);
  return () => clearInterval(id); // must be called on cleanup
}
Real-world example A React component that starts a setInterval() in useEffect but forgets to return a cleanup function calling clearInterval().

Common follow-ups: Does the same leak risk apply to setTimeout() used for repeated polling via re-scheduling itself?

Event Loop & Concurrency

How does using WeakMap/WeakSet as a design choice actively prevent certain classes of memory leaks?

Advanced
Because WeakMap/WeakSet hold their object keys/values weakly, associating metadata with an object this way never by itself prevents that object from being garbage collected — unlike a regular Map or array, which would keep the object alive as long as the cache itself exists, even after nothing else references it.
// Regular Map: keeps 'el' alive forever, even after removal from DOM
const metaMap = new Map();
metaMap.set(el, { clicks: 0 });

// WeakMap: 'el' can still be GC'd once removed and unreferenced elsewhere
const metaWeakMap = new WeakMap();
metaWeakMap.set(el, { clicks: 0 });
Real-world example Choosing WeakMap specifically to attach UI state to DOM elements that may be dynamically added and removed many times.

Common follow-ups: Are there real performance trade-offs to using WeakMap over Map, beyond the GC behavior?

Map Set WeakMap & WeakSet