Suggest an editImprove this articleRefine the answer for “Garbage collector (GC)”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**The garbage collector is the part of the runtime (V8, SpiderMonkey) that automatically frees the memory of objects that are no longer reachable from the program's roots: global objects, the call stack, live closures, the DOM tree.** The base algorithm is mark-and-sweep: walk the object graph from the roots marking everything alive, then free everything unmarked, sometimes with compaction. Modern collectors are generational (a fast minor GC for new objects, a costlier major GC for old ones), incremental and concurrent, so that pauses stay short. Reference counting is not used because it breaks on cycles. **Key point:** the GC is non deterministic, so you control your references rather than the collector: remove event listeners and timers, bound your caches, use `WeakMap`/`WeakSet`, and do not keep stray references to DOM nodes.Shown above the full answer for quick recall.Answer (EN)Image**The garbage collector (GC) is the part of the runtime (V8 in Chrome and Node.js, SpiderMonkey in Firefox and so on) that automatically frees the memory occupied by objects that can no longer be reached from the program's roots.** The main criterion is reachability: if an object is reachable from the roots (global objects, the current call stack, registers, live closures, the DOM tree and so on) it is alive; if it is unreachable, its memory can be freed. ## Theory ### TL;DR - The GC removes **unreachable** objects, not "unneeded" ones: the only criterion is reachability from the roots. - The base algorithm is **mark-and-sweep**, often with **compaction** to fight fragmentation. - Modern implementations are **generational** (young and old), **incremental** and **concurrent**, to reduce pauses. - **Reference counting** is not used in JS: it breaks on cycles such as `A -> B -> A`. - `WeakMap` / `WeakSet` do not prevent collection, `FinalizationRegistry` gives you a callback after collection but with no timing guarantees. - For a developer the key skill is **managing references**: listeners, timers, caches, closures, DOM nodes. ### Quick example ```javascript let user = { name: 'Alice' }; // the object is reachable from a root through user let admin = user; // a second reference to the same object user = null; // the first reference is gone, but the object is alive: // admin still holds it, so the GC will not free it admin = null; // now the object is unreachable and may be collected // (exactly when is up to the runtime, not your code) ``` ### How the GC decides what to delete #### Reachability The roots are what the runtime can always access: the global object, variables on the current call stack, registers, live closures, the DOM tree. Everything that can be reached from the roots through a chain of references counts as alive. Everything else is garbage. #### Mark-and-sweep 1. **Mark:** walk the object graph from the roots and mark everything reachable. 2. **Sweep:** walk the heap and **free** everything **unmarked**. On top of that the runtime may perform **compaction**: moving the survivors together to remove memory fragmentation. #### Why not reference counting Reference counters break on **cycles** (`A -> B -> A`): two objects hold each other, the counters never drop to zero, and you get a leak. That is why JS uses **tracing** collectors (mark-and-sweep or mark-compact), for which a cycle is not a problem: if the whole component is unreachable, it gets collected. ### Generational, incremental and concurrent GC #### Generational GC The observation is that "most objects die young". Hence: - **Young generation** (nursery, new space): new objects land here, collections are frequent and fast (**minor GC**, **scavenging**). - **Old generation** (tenured, old space): objects that survived several minor collections are **promoted** here, collections are rarer but more expensive (**major GC**). Technically this is implemented with: - **bump-pointer allocation** (very fast sequential allocation), - **copying GC** for the young generation: two semispaces, live objects are copied from the from-space into the to-space, - **mark-sweep** or **mark-compact** for the old generation. #### Write barrier and remembered set So that the young and old generations can reference each other correctly, the runtime uses a **write barrier** together with **card marking / a remembered set**: a cheap metadata write whenever an "old to young" reference is created, so that the minor GC knows which old objects may be holding young ones. #### Tri-color marking and short pauses A full stop-the-world collection with long pauses is unacceptable both in a UI and on a server, so runtimes use: - **Tri-color marking (white, grey, black):** a formal marking model that allows the graph walk to advance **incrementally**. - **Incremental GC:** marking is split into small chunks, and between them the application keeps running. - **Concurrent GC:** part of the work (marking, for example) runs **in parallel** with the application on another thread. - **Parallel marking and sweeping**, plus **compaction** with pause minimisation. The idea is simple: reduce jank and freezes by trading them for small, predictable pauses. ### How it works in V8 #### Heap spaces The heap is split into spaces: **new space** (the semispaces of the copying GC), **old space** (mark-sweep or mark-compact), **code space**, **map space**, and **large object space** (where objects are not moved). #### Minor GC and major GC - **Minor GC (Scavenger):** quickly copies live objects from the from-space into the to-space, and those that survived N collections get **promoted** into the old space. - **Major GC:** precise marking, then sweep or compact, incremental and concurrent in order to shorten pauses. - The details: write barriers, remembered sets, parallel marking and sweeping, heuristics around memory pressure and fragmentation. #### When the GC runs - There is no free room for a new allocation (allocation failure). - Memory pressure from the OS or the host. - Heuristics based on heap growth and the frequency of minor collections. - In DevTools you can trigger a collection by hand, but only for debugging. ### Weak references and finalization - **WeakMap and WeakSet:** the keys (for `WeakMap`) and the elements (in `WeakSet`) **do not prevent collection**. As soon as the object is unreachable everywhere else, the GC **may** free it and the entry disappears "by itself". This is useful for caches and memoization without leaks. - **FinalizationRegistry:** lets you run a callback *after* an object has been collected. Important warnings: the timing is **non deterministic**, the order is not guaranteed, and the call may never happen at all (for example when the process shuts down). You must not rely on it for logic, only for "soft" side effects such as clearing an external cache. An example of a safe cache: ```javascript const cache = new WeakMap(); // the key is an object, the value is the computation function heavy(obj) { if (cache.has(obj)) return cache.get(obj); const val = compute(obj); cache.set(obj, val); return val; } ``` ### Memory leaks: practice and diagnostics #### Typical sources of leaks 1. **Long lived references:** caches, singletons, closures, global structures, stored DOM nodes. 2. **Events and timers:** a handler or a `setInterval` that was never removed, dangling `Promise` references. 3. **DOM and JS cycles:** you keep a reference to a node removed from the DOM, or to its closure. 4. **Unbounded structures:** a `Map` or an array that grows and is never cleared. #### What to do - Remove **event handlers** and **timers**: ```javascript const handler = () => {}; el.addEventListener('click', handler); // ... el.removeEventListener('click', handler); ``` - For caches keyed by objects use `WeakMap` or `WeakSet`. - Avoid unnecessary global and module level references. - Be careful with closures: do not capture large objects you do not need. - Bound the size of caches (LRU, for instance) and clear them. - In React, watch the **cleanup** function in `useEffect`. #### Diagnostic tools - **Chrome DevTools, the Performance and Memory panels:** heap snapshots, allocation instrumentation, looking for "Detached HTML nodes" and growing retainers. - `performance.memory` (browser, experimental), rough telemetry. - **Node.js:** `--inspect`, heap snapshots, `clinic` and `heapdump`, profilers, the `--max-old-space-size` flag. - **Test idea:** run the scenario N times, and memory should stabilise. Constant growth is a leak signal. #### Limits worth remembering - **The GC is non deterministic:** you do not control the exact moment of collection. - **Finalizers** are not guaranteed to run before the process exits or the tab closes. - **Long pauses** are still possible, although modern collectors try hard to keep them short. ### Common mistakes - **Thinking the GC removes "unneeded" objects.** There is only one criterion: unreachability. An object with even one live reference from a root will not be collected, however useless it is to you. - **Believing that a reference cycle causes a leak.** That is a reference counting problem, not a tracing GC problem: an unreachable cycle is collected without trouble. - **Relying on `FinalizationRegistry` to release resources.** The callback may never run, so close files, sockets and subscriptions explicitly. - **Treating `delete obj.prop` or `x = null` as a command to the collector.** It only drops one reference, and collection happens when the runtime decides. - **Not removing listeners and `setInterval` when a component is destroyed.** A handler holds both the DOM node and its whole closure, which is the most common leak in an SPA. - **Using a plain `Map` as an object keyed cache.** It holds keys strongly, so the objects live as long as the cache does, and that scenario needs a `WeakMap`. - **Chasing micro optimisations instead of measuring.** Take a heap snapshot and compare retainers first, and only then change the code.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.