Before getting into memory leakage, an understanding of memory management is necessary. It happens manually in languages like C and C++. For example in C, whenever you want to allocate dynamic memory, you need to use malloc(). Later it is only the responsibility of the developer to free that using free().
In nodejs we need not manually handle memory management, instead garbage collector takes care of it. Garbage collector is an algorithm which runs periodically and collects unused space from heap. There are some cases where this garbage collector is unable to collect unused memory which leads to memory leakage!
So there cannot be an algorithm that finds all unwanted memory. Thus it becomes the responsibility of developer to write code in such a way that all unuse memory will be garbage collectes.
Garbage collection in memory
The two algorithms widely used for garbage collection are the reference counting algorithm and the mark and sweep algorithm. The data for our application code will be present in the stack and heap memory. The heap memory stores objects and functions. The Stack memory stores primitive values and references. The data stored in stack gets a fixed amount of memory. In the below code, the allocation of memory for the object happens in the heap and the reference will be present in the stack.
Reference counting algorithm
In this algorithm, if any object stored in the heap does not have a reference to it, then the release of that memory happens. So when the scope of the object ends, then the reference to the object will be broke and will be garbage collected.
The removal of the object from memory happens if the garbage collector finds that the object present in the heap has no reference to it.
For example, consider the below case where each object refers to the other.
So the objects can also be referres to cyclically within the heap memory. In this case, this kind of object will not have a reference from the stack. So it will be impossible for the garbage collector to remove it from the heap. The following diagram shows how it will be reference in memory.
Mark and sweep algorithm
This algorithm starts from this root and finds all objects which are reachable from the root. Other objects which are unreachable from the root will be removed from memory. So even if there are cyclic object references in the code, garbage collection will happen.
Reasons for memory leakage
1. Global Objects
Global variables are the most common memory leakage, which will always be present in the space until the program completes execution. This can be an issue if your application has to run always! After the use of the variables, they can be cleared by setting them to null. Similarly, for functions after their use, they can be set to null to free heap memory.
2. Unknowingly adding data or memory to the global object
Sometimes we try to modify a local variable referencing a global variable. Though on first look it appears to be changing only the local variable, it modifies the global variable too.
In the below code, we perform some operations on the local array flash. But unknowingly we add an extra element to the global array ‘arr’ in every iteration. This subsequently becomes a more enormous array and the garbage collection will never happen.
3. Chaining of object references
Chaining object references to a global object does not allow the garbage collection of local objects. In the below code, the function sample runs every one second and assigns the global object to the local object. The global object is redefine where it has reference to its previous object through ‘newObj’. So the global object will have reference to all of its previous objects. This results in a rapid increase in space and this prevents garbage collection by GC.
We can overcome this problem by setting the localObj to null at the end of the function. This makes it lose the reference to the previous object and eventually, GC will free those objects.
The node-inspector tool will be helpful to monitor the ROM usage of the application. From the image below we can see that on monitoring this code using node-inspector, a drastic increase in space usage happens. Refer to the snapshot size shown in the left pane and the objects newly created every second in the center pane.
A similar kind of issue can be seen through closures where the closures in the same scope share reference of the variables used inside. For example in the below code, the function inner is a closure that has reference to ‘localObj’. There is also another closure ‘newFunc’ which is an empty function. As closures inner and newFunc are in the same scope, newFunc also gets the reference of ‘localObj’. So globalObj will get the reference of its old object through newFunc.
We can overcome this problem by setting the localObj to null at the end of the function. Or, removing the reference of localObj from the function inner will solve the issue.
The heap and stack memory will look like the below. On running the above code, the previous object memory will still reside in it and cannot be garbage collects. The solid arrow represents the reference to the old object memory.
Note: Heap memory will not look as shown in the image. The allocation will happen randomly, not like as shown in the image.
To learn more about Engineering topics visit – https://engineering.rently.com