← Home ← Codex ← DEBT ← Engine
Browse by Category
+ added · updated 7d
← Back to glossary

Memory Leaks — Closures, Detached DOM

JavaScript ES2015 Advanced
debt(d7/e5/b5/t7)
d7 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'only careful code review or runtime testing' (d7). Chrome DevTools is listed in detection_hints.tools, but it requires manual heap snapshot inspection and pattern recognition — not automated. The code_pattern hint (addEventListener) catches only one common case; other leaks (circular references, cache bloat, detached DOM storage) are silent until memory profiling reveals them.

e5 Effort Remediation debt — work required to fix once spotted

Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix mentions AbortController and WeakMap replacements, which are single-concept swaps (e3-worthy), but memory leaks typically infect multiple event handlers, lifecycle hooks, and closure scopes across a component or module. Fixing them comprehensively requires audit and refactor of several interdependent pieces.

b5 Burden Structural debt — long-term weight of choosing wrong

Closest to 'persistent productivity tax' (b5). Memory leak patterns (addEventListener without cleanup, closure-captured DOM refs, unbounded caches) are easy to introduce during feature development and accumulate silently. They don't rewrite the architecture (b9), but they slow down long-running SPAs and Node processes, forcing developers to become vigilant about lifecycle cleanup patterns, testing strategies, and monitoring. Every async callback and component unmount becomes a potential pitfall.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap' (t7). The core misconception ('GC makes leaks impossible') directly contradicts how GC actually works and is reinforced by other languages/contexts where manual memory management is explicit. Many developers assume GC is sufficient, leading to the 'obvious' mistake of not cleaning up event listeners or DOM references. Common_mistakes emphasizes the disconnect between expectation (GC handles it) and reality (references must be explicitly cleared).

About DEBT scoring →

TL;DR

JavaScript memory leaks occur when references are accidentally retained — common causes: closures holding large objects, detached DOM nodes, forgotten event listeners, and growing Maps/Sets.

Explanation

JavaScript uses a mark-and-sweep GC — objects are freed when no longer reachable. Leaks occur when references are accidentally kept: (1) Closures that capture large outer scopes, (2) Event listeners on removed DOM nodes keep the nodes alive, (3) Global variables holding data that grows, (4) setInterval/setTimeout callbacks not cleared, (5) DOM references stored in JS after the element is removed, (6) Large Maps/Sets used as caches with no eviction. Detecting: Chrome DevTools Memory tab, heap snapshots, allocation timelines. Node.js: --inspect + heapdump.

Common Misconception

JavaScript has GC so memory leaks are impossible — GC only collects unreachable objects. As long as a reference exists, the object is kept alive.

Why It Matters

Memory leaks in SPAs and long-running Node processes cause gradual performance degradation and eventual crashes that are extremely difficult to debug.

Common Mistakes

  • Not removing event listeners when components unmount.
  • Storing DOM references in closures after elements are removed.
  • Using regular Map/Set as a cache without size limits or TTL eviction.
  • Circular references between JS objects and DOM nodes.

Code Examples

✗ Vulnerable
// Event listener leak:
const button = document.getElementById('btn');
function handleClick() { /* captures large data */ }
button.addEventListener('click', handleClick);
// Later: button removed from DOM but listener keeps it alive
✓ Fixed
// Remove listeners when done:
const controller = new AbortController();
button.addEventListener('click', handleClick, { signal: controller.signal });
// To remove all listeners:
controller.abort();

// WeakMap for DOM associations — GC-friendly:
const meta = new WeakMap();
meta.set(element, { clicks: 0 }); // element can be GC'd

Added 22 Mar 2026
Edited 5 Apr 2026
Views 128
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings S 0 pings M 2 pings T 0 pings W 1 ping T 1 ping F 0 pings S 0 pings S 1 ping M 0 pings T 0 pings W 0 pings T 0 pings F 1 ping S 2 pings S 0 pings M 0 pings T 1 ping W 0 pings T 1 ping F 1 ping S 0 pings S 0 pings M 0 pings T 1 ping W 0 pings T 1 ping F 0 pings S 0 pings S 0 pings M
No pings yet today
No pings yesterday
Amazonbot 11 PetalBot 11 Google 7 Ahrefs 7 SEMrush 6 Bing 5 Perplexity 4 Scrapy 3 Unknown AI 2 Claude 2 Twitter/X 2 Applebot 2 Brave Search 2 ChatGPT 1 Majestic 1 Meta AI 1 Sogou 1
crawler 64 crawler_json 2 pre-tracking 2
🧱 FUNDAMENTALS — new to this? Start with the ground floor.
JavaScript javascript The programming language of the browser — it reads and modifies the page (the DOM), reacts to user events, and fetches data without reloading.

JavaScript is the only language browsers execute, so every interactive behaviour on the web goes through it. Its two defining traits — single-threaded event loop and loose typing (== coercion) — explain the majority of both its bugs and its design patterns.

💡 Default to const, use === always, and reach for let only when a value genuinely reassigns.

Ask Codex about JavaScript →
DEV INTEL Tools & Severity
🟡 Medium ⚙ Fix effort: High
⚡ Quick Fix
Use AbortController to bulk-remove event listeners. Use WeakMap/WeakRef for DOM metadata. Clear intervals/timeouts. Use Chrome Memory tab to take heap snapshots.
📦 Applies To
javascript ES2015 web cli
🔗 Prerequisites
🔍 Detection Hints
addEventListener\(
Auto-detectable: ✗ No chrome-devtools
⚠ Related Problems
🤖 AI Agent
Confidence: Low False Positives: High ✗ Manual fix Fix: High Context: File Tests: Update
CWE-401 CWE-400


✓ schema.org compliant