Garbage Collection in Javascript

We have understood what memory is, and how memory is allocated and used in the previous article on Memory management in Javascript.
Most of the memory leaks and memory issues happen because the allocated memory that is no longer in use is not disposed off but rather released as expected. Let's try to understand how Javascript releases unused memory.
High-level languages embed a piece of software called the Garbage collector in them which helps check the memory allocations and release any such memory that is no longer referenced. The software that does this is called as the "Garbage collector" and the process of doing so is called "Garbage collection".
References
The main concept the garbage collectors lean-to, to be able to perform Garbage Collection (In short called as "GC" - popular term), is references. In the context of memory, an object is said to "reference" another if the former has access to the latter. What does that mean? Let's understand with an example.
const father = {
name: "Father",
son : son
};
const son = {
name: "Son"
}
Here, the object father is said to reference son, since the property son inside the father object references the object son.

What is Reachability?
An object or an entity is said to be reachable, if:
- The current function, variables, parameters, and other declarations.
- Other functions in the chain of calls.
- Global entities - Global variables and declarations.
- Internal - such as prototypes are always referenced by other entities.
- Any value is considered reachable, if, it is reachable from the root by reference or chain of references. Imagine it to be like a line of people holding hands.

In the above image, if they were to start walking, without letting go of their hands, they are connected, right? That means, if the first person in the line wanted to share a message with the last, it could flow down the line by transmitting the message to each other, similarly, a reference has in a way "access" to the object it is referring to.
There are mainly two kinds of Garbage-collecting Algorithms:
- Reference-counting Garbage collection.
- Mark-and-sweep algorithm.
But wait, Before getting to the two kinds of Garbage collection algorithms, lets understand the basics of how Garbage collection happens.
let person = {
name: "Shrikanth"
}
Creating this object in the outmost scope, i.e. just create a .js file and adding the above code, would be to create the person object in the global scope.
Here the variable person
holds the object - lets call it the shrikanth
object, why not!
Similar to taking a bowl and placing our favorite Laddu. We could compare the Laddu to the shrikanth
object and person
to the container.
Say, we were to set the person
to null, such as:
person = null;
Would be equivalent to dropping the Laddu on the floor, making space for another Laddu in the container none the less, but the Laddu dropped on the floor would quickly be picked and cleaned up, right? Or shall I say - Garbage collected? ( The Aha moment!)
let person = {
name: "Shrikanth"
}
let author = person;
Now if we were to set :
person = null;
What do you think happens to the object? It is still referenced, not by person anymore but still by admin, hence it will not be Garbage collected, since admin
could be referenced somewhere in the code.
Now let's consider a slightly complicated example, nothing too hard, I am going to make it easy for you understand.
const father = {
name: "Father",
son : son
};
const son = {
name: "Son"
father: father
}
const family = {
father: father,
child: son
}
In the above, we see that both the objects are interlinked.
Now the relationship would look something like:

Now all of the objects would be reachable since all of the objects are referenced and have a path to reach the family object which is the root object here.
Now what if we delete some of the references:
delete family.father;
delete son.father;
Now, with this change, even though father
references son
, nothing references the father
. father
only has outgoing references but no incoming references. that means father
references other objects but nothing references father
, since nothing references father
, it will be Garbage collected.
Why do outgoing references not matter:
Even though father references to son
since nothing uses father
it does not matter what it references to. Imagine there is a water tank and a pipe. But there is no connection between the tank and the pipe, even though the pipe has another pipe connected to it, it does not matter because the first pipe is not connected to the tank and no water will ever reach the pipe, the same way, objects which are not referenced are like islands and the Garbage collector finds them and sweeps them.
Now all the objects that are connected to father would also be removed if they have no other reference, just like all the connected pipe to the first once, since they have no other connection.
Reference-counting Garbage collection
The simplest Garbage collection algorithm! Let us consider the above example,
let person = {
name: "Shrikanth"
}
Here if we assign person to null as we discussed above, there is no reference to the shrikanth
object hence reference-counting algorithm would consider the memory reclaimable, but in the second example:
const father = {
name: "Father",
son : son
};
const son = {
name: "Son"
father: father
}
const family = {
father: father,
child: son
}
Say the above data is used inside a function call, once the function is done executing, the data is no longer needed, right? So ideally, the memory consumed is reclaimable, but the reference counting algorithm checks for references, and since father
and son
reference each other, at least one reference is present for both objects although both the objects are not referenced anywhere else after the function call, it still does not reclaim the memory, hence leading to a memory leak.
Mark-and-sweep algorithm
Rather than identifying an object by how many references it has, the mark-and-sweep algorithm uses the concept of reachability ( discussed above ).

The algorithm first identifies the root nodes, think of a tree data structure with roots. Roots in Javascript are essentially global objects. Since everything in the code eventually has to be consumed by global items.
The GC first identifies the roots of the project, once done it finds all the objects that are referenced or are present in the chain of reference from the roots. These objects are called reachable objects. The algorithm is an improvement over the last one, in a way, since an object that is not referenced is unreachable too. However the opposite is not true since there could be two objects that reference each other but could be unreachable.
Currently, all the Javascript engines use the mark and sweep algorithm. Over the years the GC algorithm has had different versions such as generational, incremental, concurrent, and parallel garbage collection.
Working of the Garbage collector - Mark-and-sweep algorithm
- Identifying the roots: The algorithm starts with identifying the roots, the roots are essentially the nodes/entities that are either currently in use, or are global variables or accessible in the global scope.
- Marking: The algorithm then traverses each of these nodes till it reaches all of the leaf nodes. While it does the traversal, it checks if the object is still in use, if the object is in use, it is marked as required.
- Release the memory: Now, the objects and variables that are not in use anymore are essentially garbage and garbage collected and the memory is released, essentially the container with the Laddu is now empty since we probably just had a good snack time break!
This is an improvement over the reference counting algorithm and is more efficient since an object with zero reference is an unreachable object. As of 2024, all the major browsers have shipped the mark-and-sweep algorithm.
The issue that we discussed earlier where there are two objects father
and son
which are interlinked but are not referenced from any of the root nodes does not become a problem anymore, they would together be garbage collected since both are unreachable from any of the root nodes even though they are interlinked.
Trade-offs
Although GC is extremely helpful and an integral part of the engine, they can be non-deterministic, we don't know or cannot control when the GC would run and clean up the memory, hence this could lead to the program consuming more memory than usual during certain phases. Although, there is a common pattern of the engine to run the GC when allocations happen, so it's not that bad either.
What is a memory leak?

A memory leak in JavaScript occurs when a program retains references to objects that are no longer needed, preventing the garbage collector from freeing up memory. This can happen when variables, event listeners, or closures continue to hold onto references to objects even after they are supposed to be out of scope. As a result, the memory occupied by these objects cannot be reclaimed, leading to increased memory consumption over time. This inefficient memory management can cause a web application to become slow or unresponsive, as the system struggles with an ever-growing memory footprint.
Memory leaks can be particularly challenging to diagnose and fix because they often manifest over time, making them less apparent during initial testing. Common causes of memory leaks include global variables, unintentional retention of DOM elements, and improper use of closures that inadvertently keep references to objects. To identify and address memory leaks, developers can use tools such as browser developer tools or dedicated memory profiling tools to monitor and analyze memory usage patterns. By understanding and addressing these issues, developers can ensure their applications run efficiently and maintain a smooth user experience.
There are mainly 4 ways that lead to memory leaks:
- Global variables: Variables that are put in the window object
- Timers and callbacks: Timers such as setIntervals that do not have an exit condition. More about Timers is discussed in the Browser Object Model section.
- Closures: Incorrect use of closures.
- DOM references: Copying DOM elements inside dictionaries can create copies of the nodes that are in the DOM.
It is important to understand the concepts of Javascript such as Garbage collector in-depth to be able to become a strong front-end developer, Not to worry since we here at LearnYard are here to help you achieve the same!