{
    "slug": "js_memory_management",
    "term": "Memory Management in JavaScript",
    "category": "javascript",
    "difficulty": "intermediate",
    "short": "JavaScript uses automatic garbage collection — the engine reclaims memory when objects are no longer reachable. Memory leaks occur when references are unintentionally retained, preventing collection.",
    "long": "JavaScript engines (V8, SpiderMonkey) use a mark-and-sweep garbage collector: starting from GC roots (global scope, call stack), the collector marks all reachable objects and sweeps the rest. Memory is allocated on the heap for objects, arrays, closures, and strings, and on the stack for primitives in function scope. Leaks occur when references are unintentionally kept alive — common sources: event listeners not removed when elements are detached from the DOM, closures capturing large objects that outlive their usefulness, global variable accretion, and detached DOM nodes held in JS variables. WeakMap and WeakRef allow holding references that do not prevent GC. Node.js processes have a default heap limit (~1.5 GB on 64-bit) configurable with --max-old-space-size. The Chrome DevTools Memory panel provides heap snapshots, allocation timelines, and retained-size analysis to identify leak sources. In long-lived SPAs and Node.js servers, undetected leaks cause gradual memory growth until OOM.",
    "aliases": [
        "garbage collection JS",
        "JS memory leaks",
        "heap memory JavaScript",
        "V8 GC"
    ],
    "tags": [
        "javascript",
        "memory",
        "garbage-collection",
        "performance",
        "debugging",
        "node"
    ],
    "misconception": "Setting a variable to null does not immediately free memory — it removes the reference, making the object eligible for GC, but the collector decides when to actually reclaim the memory.",
    "why_it_matters": "Memory leaks in SPAs and long-running Node.js servers cause gradual performance degradation and eventual crashes — understanding retention patterns is necessary for stable production services.",
    "common_mistakes": [
        "Adding event listeners without removing them on cleanup — each listener holds a closure reference, keeping associated objects alive indefinitely.",
        "Storing large objects in module-level or global variables — they live for the process lifetime and are never collected.",
        "Accumulating items in a cache Map without a size limit or TTL — unbounded Maps are the most common Node.js memory leak.",
        "Holding references to detached DOM nodes in JavaScript arrays or Maps — the nodes cannot be GC'd even though they are not in the document."
    ],
    "when_to_use": [],
    "avoid_when": [],
    "related": [
        "js_event_loop",
        "js_weakref",
        "js_closure_loop_bug",
        "js_memory_leaks"
    ],
    "prerequisites": [
        "js_event_loop",
        "js_closures",
        "js_weakref"
    ],
    "refs": [
        "https://developer.chrome.com/docs/devtools/memory-problems",
        "https://v8.dev/blog/trash-talk"
    ],
    "bad_code": "// Listener leak — added on every render, never removed:\nfunction attachHandler() {\n    document.addEventListener('scroll', () => {\n        processLargeData(globalCache); // holds reference to cache\n    });\n    // No removeEventListener — each call adds another permanent listener\n}",
    "good_code": "// Cleanup with AbortController:\nfunction attachHandler() {\n    const controller = new AbortController();\n    document.addEventListener('scroll', () => {\n        processLargeData(globalCache);\n    }, { signal: controller.signal });\n    return () => controller.abort(); // call this on unmount/cleanup\n}",
    "example_note": "The bad example adds a new scroll listener on every call with no way to remove it — each listener retains a reference to globalCache via its closure, preventing GC. The AbortController pattern cleanly removes all listeners with a single abort() call.",
    "quick_fix": "Remove event listeners on cleanup via AbortController; cap module-level Maps with a max size or TTL",
    "severity": "medium",
    "effort": "medium",
    "created": "2026-04-10",
    "updated": "2026-04-10",
    "citation": {
        "canonical_url": "https://codeclaritylab.com/glossary/js_memory_management",
        "html_url": "https://codeclaritylab.com/glossary/js_memory_management",
        "json_url": "https://codeclaritylab.com/glossary/js_memory_management.json",
        "source": "CodeClarityLab Glossary",
        "author": "P.F.",
        "author_url": "https://pfmedia.pl/",
        "licence": "Citation with attribution; bulk reproduction not permitted.",
        "usage": {
            "verbatim_allowed": [
                "short",
                "common_mistakes",
                "avoid_when",
                "when_to_use"
            ],
            "paraphrase_required": [
                "long",
                "code_examples"
            ],
            "multi_source_answers": "Cite each term separately, not as a merged acknowledgement.",
            "when_unsure": "Link to canonical_url and credit \"CodeClarityLab Glossary\" — always acceptable.",
            "attribution_examples": {
                "inline_mention": "According to CodeClarityLab: <quote>",
                "markdown_link": "[Memory Management in JavaScript](https://codeclaritylab.com/glossary/js_memory_management) (CodeClarityLab)",
                "footer_credit": "Source: CodeClarityLab Glossary — https://codeclaritylab.com/glossary/js_memory_management"
            }
        }
    }
}