
Garbage collector doing its job
How Node.js allocates memory
Node runs on the V8 JavaScript engine, which handles memory allocation. The entirety of memory used for a running Node application is held in what is known as a "Resident Set Size" (RSS).
** Resident Set Size: This represents the amount of memory currently held in the RAM for a particular Node.js process. It includes both the JavaScript code, native code (C++), and any other resources held by the process.
So, the RSS includes:
- Code memory: Holds the compiled representations of the code. It includes both the code you write and any libraries or dependencies your code uses (both javascript and C++ code). It's not garbage collected, however it's managed by V8 engine, which may involve optimizing the generated machine code, recompiling code when necessary (for instance, when code optimization strategies change), and deallocating memory when functions are no longer used.
- Map Space: Holds type information for objects, and the maps themselves are structures necessary for V8's internal workings to optimize object representation and access. The structures in the Map Space are managed by V8's memory management system and are not explicitly garbage collected.
- Stack memory: Memory allocated for the stack, which contains function call information (e.g function parameters), local variables created during function calls, etc... It works in a FILO way (first in, last out), and that is how memory is released.
- Heap memory: The dynamically allocated memory used for storing objects and data structures during runtime. This is the part of memory that is garbage collected and the one that might have problems with memory leaks.
Heap memory
As you can se the V8's heap is organized into different spaces.
- New Space (or Young Generation): This area is where new objects are initially allocated. It's optimized for fast allocation and is divided into two semispaces (from-space and to-space). Objects are allocated in the from-space, and when a garbage collection process occurs (often using a process called Scavenge), the live objects are copied to the to-space. This helps in identifying and clearing out short-lived objects quickly.
- Old Space (or Old Generation): Objects that survive multiple garbage collection cycles in the New Space are eventually moved to the Old Space. This space is optimized for larger, long-lived objects. The garbage collection process in this space is more complex and typically involves algorithms like Mark-Sweep or Mark-Compact to free up memory occupied by unreachable objects.
- Large Object Space: This space is specifically dedicated to larger objects that exceed a certain size threshold. These large objects are not stored in the New Space but directly allocated in the Large Object Space. Managing memory for large objects differs from regular object allocation and has its own set of considerations during garbage collection.
How Node.js frees up Heap memory
Garbage Collection periodically purges unused memory by removing objects that are no longer required from the heap. The GC process in Node.js is a combination of two algorithms (One for minor gc and another for major gc). Both processes ensure that unused objects are cleaned up, making more space available for new objects and keeping the application running.
Scavenger (Minor GC)
The Scavenger algorithm looks for short-lived objects in a smaller area of memory called the New Space. These short-lived objects are expected to disappear quickly. Think of the New Space as a temporary storage area for newly created objects. The Scavenger algorithm regularly checks the New Space for unused objects. If an object is no longer needed, it is removed from memory. The objects that are still in use are moved to a different area of memory called the Old Space.
Mark-Sweep & Mark-Compact (Major GC)
The Mark-Sweep and Mark-Compact algorithms take care of the Old Space, which is a larger area of memory where long-lived objects are stored. Unlike the New Space, these objects survive for a longer time.
The Mark-Sweep algorithm scans the Old Space and marks the objects that are still being used. After marking, it sweeps through the memory and removes the objects that are not marked.
Situations that might lead to a memory leak
- Global variables: Are never garbage collected
- Self-referenced objects: There is a chance that GC won't figure out the way to deal with this properly
- Objects cross-referenced: Object 1 references to object 2 and viceversa. This two objects reference each other but nothing else references to them. That would be a memory leak.
- Unclosed event listeners: This one is pretty self-explanatory. Don't forget to use "
removeListener
". - Timers and Intervals: Always remember use "clearTimeout" and "clearInterval".
- Closure Scope Retention: Retaining unnecessary closures or function scopes can also cause memory leaks. Functions that enclose variables from outer scopes and keep references to these scopes might prevent objects from being garbage collected.
Finally, a reminder of what the GC considers so it can free that memory where an object is stored:
- It is not a root object. A root object is a live object which is directly pointed to by V8 or a Global Object.
- It is not referenced by any root or other live object.
Don't be shy, leave us a comment