Deep Dive: Node.js Memory Management & Garbage Collection
Node.js runs on the V8 JavaScript engine (the same one inside Google Chrome). Understanding how V8 manages memory is crucial for building high-performance, scalable backend applications. Unlike languages like C++, where you manually allocate and free memory, V8 handles this automatically via the Garbage Collector (GC).
The Stack vs. The Heap
V8 divides memory into two primary regions:
The Stack (Static)
Used for static data, method calls, and primitive values (Numbers, Strings, Booleans). Data here has a fixed size known at compile time. It follows LIFO (Last In, First Out). Access is extremely fast.
The Heap (Dynamic)
Used for storing Objects, Arrays, Functions, and Closures. The size is dynamic and can grow. The Stack only holds a "reference" (pointer) to the object's location in the Heap.
How Garbage Collection Works (Mark and Sweep)
V8 manages the Heap using a concept called Reachability. The "Root" is usually the global object or the currently executing function's stack. Periodically, the GC runs:
- Marking Phase: The GC starts at the roots and traverses all references. Any object it can reach is marked as "alive".
- Sweeping Phase: Any object in the Heap that was not marked is considered unreachable (garbage). V8 releases this memory.
- Compacting: V8 may move surviving objects to reduce fragmentation.
Generational Collection: New Space vs. Old Space
V8 assumes that "most objects die young". Therefore, it splits the Heap into two generations:
- New Space (Young Generation): Where new objects are born. The GC here ("Scavenge") is very fast and frequent.
- Old Space (Old Generation): Objects that survive two GC cycles in New Space are promoted here. The GC here is slower and runs less often.
Common Memory Leaks in Node.js
Even with a GC, memory leaks occur when you accidentally keep references to objects you don't need.
The "Accidental Global"
global.myData = new Array(1000000);Global variables are always roots, so they are never collected until the program exits.
The "Forgotten Timer"
setInterval(() => customCache.push(data), 1000);If you don't call `clearInterval`, the callback and everything it references stays in memory forever.
Pro Tip: Use the node flag --inspect to attach Chrome DevTools to your Node process. Take "Heap Snapshots" to visualize what objects are taking up space and find leaks.