let obj = { data: 'large' };
obj = null; // no more references -> eligible for garbage collection
Topics
37
ArrayBuffer, TypedArrays & Binary Data
Arrays & Array Methods
Async Iterators & Streams
Browser Storage & Web APIs
Classes & Class Syntax
Date, Time & Internationalization (Intl API)
Debugging, Testing & Tooling
Design Patterns in JavaScript
Destructuring, Spread & Rest
DOM & Events
Error Handling
ES Modules
Event Loop & Concurrency
Functional Programming
Iterators & Generators
JSON & Data Serialization
Map, Set, WeakMap & WeakSet
Memory Management & Garbage Collection
Networking: Fetch, XHR, WebSockets & CORS
Numbers, Math & BigInt
Objects, Property Descriptors & Immutability
Optional Chaining & Nullish Coalescing
Package Management, Bundlers & Transpilation (npm, Webpack/Vite, Babel)
Performance Optimization: Debouncing, Throttling & Memoization
Promises & async/await
Prototypes & Inheritance
Proxy & Reflect
Regular Expressions
Scope, Hoisting & Closures
Security: XSS, CSRF & Content Security Policy
Service Workers & Progressive Web Apps
Strings & Template Literals
Symbols & Well-Known Symbols
this & Binding
Types & Coercion
Web Components & Custom Elements
Web Workers & Multithreading
Memory Management & Garbage Collection
10 questions found
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.
Real-world example
Understanding why setting a variable to null can help release a large object sooner, though it's rarely required.
Types & Coercion
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.
Debugging
Testing & Tooling
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.
DOM & Events
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.
Scope
Hoisting & Closures
What is the difference between the generational garbage collection strategy's 'young' and 'old' generations?
IntermediateMost 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.
Performance Optimization: Debouncing
Throttling & Memoization
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).
Design Patterns in JavaScript
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.
Map
Set
WeakMap & WeakSet
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.
Debugging
Testing & Tooling
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().
Event Loop & Concurrency
How does using WeakMap/WeakSet as a design choice actively prevent certain classes of memory leaks?
AdvancedBecause 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.
Map
Set
WeakMap & WeakSet