Lesson 28: Mastering JavaScript Garbage Collection with challenges!

manoj ymkmanoj ymk
5 min read

🚀 What is Garbage Collection?

Garbage Collection (GC) is JavaScript’s built-in memory cleanup mechanism. It automatically reclaims memory occupied by objects no longer “reachable” or “in use,” helping prevent memory leaks.

🧠 Key Principle: Reachability

  • JS only keeps values in memory if they’re reachable from the “roots.”

  • Roots include:

    • Global variables

    • The current execution context (stack)

    • Closures in active functions

✅ If an object is not reachable → It’s garbage → GC removes it.


📦 Example 1: Simple Reference Loss

let user = { name: "John" };
user = null; // 'John' is now unreachable and collected

🔗 Example 2: Two References

let user = { name: "Ann" };
let admin = user;
user = null; // Object still reachable via 'admin'
admin = null; // Now it's unreachable → collected

💍 Example 3: Interlinked Objects

function marry(man, woman) {
  man.wife = woman;
  woman.husband = man;
  return { father: man, mother: woman };
}

let family = marry({ name: "John" }, { name: "Ann" });

delete family.father;
delete family.mother.husband;
// John now has no incoming reference → garbage

🗂️ Diagram — Mark-and-Sweep (Simplified)

Roots
  |
Global → Object A → Object B → Object C

If Object A is unlinked:
  Object B, C become unreachable
  GC sweeps and deletes them

🔹 2. Fill Any Gaps (Advanced Concepts, Edge Cases)

🔍 What Devs Often Miss:

  1. Outgoing references don’t keep an object alive — Only incoming ones matter.

  2. Closures: A function can retain variables even after it returns. These remain in memory if still referenced.

     function outer() {
       let secret = "💥";
       return function inner() { console.log(secret); };
     }
    
     const fn = outer(); // `secret` is still reachable!
    
  3. Memory Leaks Still Happen:

    • Forgotten timers (setInterval)

    • Detached DOM nodes still referenced

    • Closures capturing heavy objects


⚙️ GC Algorithms (Behind the Scenes)

AlgorithmWhat It Does
Mark-and-SweepMarks reachable objects → deletes others
Generational GCSeparates short-lived and long-lived objects
Incremental GCSplits work into chunks to avoid pauses
Idle-Time GCRuns GC only when the main thread is idle

🧠 Engines like V8 use a hybrid of these for speed.


🔹 3. Challenge Me Deeply

🟢 Basic

  1. Create an object and assign it to two variables. Null one and observe memory.

  2. Create an object that has a reference to itself. Explain if it's garbage.

  3. Build a function returning a closure that captures a variable. Will GC collect it?

🟡 Intermediate

  1. Create a circular structure with three objects. Write logic to break the cycle.

  2. Create a timer (setInterval) that logs a value. Make it garbage-safe.

  3. Store DOM elements in an array. Then remove from DOM. Will memory be freed?

  4. Simulate a memory leak by attaching a closure to a button’s event listener.

🔴 Advanced

  1. Create a deep closure chain. Identify when each layer becomes GC-eligible.

  2. Track retained memory in Chrome DevTools using heap snapshots.

  3. Build a custom “reference tracker” that shows whether an object is still reachable.

🎯 Bonus Brain Twister:

  • How can an object with no variables pointing to it still stay in memory?

🔹 4. Interview-Ready Questions

🧠 Conceptual

  • What does “reachability” mean in JavaScript memory management?

  • Describe the lifecycle of an object in memory.

  • Why is a circular reference not a problem in modern GC?

🔎 Debugging Scenario

function createUser() {
  let secret = "123";
  return () => console.log(secret);
}
const userFn = createUser();
// Question: Is `secret` still in memory? Why?

✅ Best Practices

✅ Do❌ Don’t
Clean up timers & intervalsLeave setInterval running indefinitely
Null out DOM refs on removalHold references to removed elements
Detach event listenersForget closures attached to DOM

🔹 5. Real-World Usage

Front-End:

  • React: Memory leaks from uncleaned effects or stale closures

  • DOM: Holding references to removed nodes causes leaks

Back-End (Node.js):

  • Holding large objects in global space (e.g., big cache)

  • Unhandled listeners (e.g., EventEmitter leaks)

Libraries:

  • React hooks (e.g., useEffect cleanup)

  • RxJS subscriptions (.unsubscribe() to avoid leaks)

  • EventEmitter: Unremoved listeners pile up memory


🔹 6. Remember Like a Pro

🧠 Mnemonic: "RRR" = Reach → Reference → Retain

  • Only Reachable values stay alive

  • References don’t equal retention — must come from a root

  • Unreferenced → Unreachable → Removed

📌 Cheatsheet:

ConceptRetained?Why?
Referenced by closureActive stack
Referenced by timerTimer root
Circular referencesGC handles them
DOM node removed but still in JS varStill reachable

🔹 7. Apply It in a Fun Way

🧪 Mini Project: Memory Leak Detector Utility

Create a tool that:

  1. Tracks object creation

  2. Lets you “unlink” references

  3. Flags uncollected memory (simulated)

Steps:

  1. Create trackObject(obj) that stores it in a WeakMap

  2. Simulate reference breaking via obj = null

  3. Show if it’s still in memory (mocked)

  4. Tie to a UI showing “Retained” or “Freed”

🔁 Extend it to:

  • Warn if timers or DOM leaks are likely

  • Show dependency graphs like Chrome DevTools


➕ Bonus: Expert Corner

🔥 Used in Open Source:

  • React, Vue, Angular: Handle GC traps around lifecycle

  • Node.js: GC tuning via --max-old-space-size

  • Chrome DevTools: Built-in GC profiler

⚠️ Common Mistakes:

  • Forgetting .removeEventListener

  • Not cleaning up RxJS subscriptions

  • Using globals for caching without purging

⚡ Performance Tip:

Avoid holding large objects longer than necessary. Split tasks using setTimeout(..., 0) to let GC run.

0
Subscribe to my newsletter

Read articles from manoj ymk directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

manoj ymk
manoj ymk