Understanding Memory Management in JavaScript: How Garbage Collection Keeps Your Code Tidy

Ayush Gupta
4 min readJun 17, 2023

--

Introduction

As developers, it’s important to understand how memory management works in JavaScript to optimise our code’s performance. In this article, we will explore the core concepts of memory allocation and garbage collection. By demystifying these processes, you’ll gain a clearer understanding of how JavaScript efficiently handles memory usage.

Memory Allocation — Heap and Stack

When JavaScript runs, it allocates memory in two main areas: the heap and the stack.

  1. Heap Memory
    The heap memory is where dynamically allocated objects are stored. This includes objects created using the new keyword, arrays, and objects referenced by variables or data structures like linked lists and trees. The heap memory is managed by the garbage collector, which automatically frees up memory when objects are no longer in use.
  2. Stack Memory
    The stack memory is used for function calls and local variables. Whenever a function is called, a new frame is added to the stack to store local variables, function arguments, and return addresses. As functions are called and return, the stack grows and shrinks accordingly. The stack memory is organised in a Last-In-First-Out (LIFO) manner, meaning the most recently added item is the first to be removed. Stack memory is generally faster to allocate and deallocate than heap memory.

To illustrate this, let’s consider few examples:

function calculateSum(a, b) {
const result = a + b;
return result;
}

const x = 5;
const y = 10;
const sum = calculateSum(x, y);

In this code, the calculateSum function and its local variable result are stored in the stack memory, as they are created during function execution. The variables x, y, and sum are also allocated in the stack memory. The object obj is stored in heap memory.

// Creating a person object
const person = {
name: 'John',
age: 30
};

// Assigning person object to another variable
const anotherPerson = person;

// Modifying the name property of anotherPerson
anotherPerson.name = 'Jane';

// Accessing the name property of person
console.log(person.name); // Output: Jane

In this example, we have a person object with properties name and age. When we assign person to anotherPerson using the assignment operator, it doesn't create a new copy of the object. Instead, anotherPerson now holds a reference to the same object in memory.

Both person and anotherPerson are reference types stored in the heap. The variables person and anotherPerson in the stack contain memory addresses (references) pointing to the actual object in the heap.

When we modify the name property of anotherPerson, it directly affects the underlying object. Since both person and anotherPerson reference the same object, accessing the name property of person after the modification will give us the updated value.

When you assign a reference type to a variable, you are creating a reference to the original object rather than creating a new copy of it. Any modifications made to the object through any of the variables referencing it will be reflected across all references.

It’s worth noting that primitive types like numbers and strings are stored directly in the stack rather than the heap. They are passed by value, meaning a copy of the value is assigned to the variable rather than a reference to the original value.

The Role of the Garbage Collector

The garbage collector is responsible for reclaiming memory that is no longer in use. It identifies objects that are no longer reachable and frees up their memory. JavaScript uses various garbage collection algorithms, and two commonly used ones are reference counting and mark-and-sweep.

Let’s consider an example to understand how the garbage collector works:

let obj1 = { name: 'John' };
let obj2 = { name: 'Jane' };

obj1 = null;

In this code snippet, two objects, obj1 and obj2, are created on the heap. Initially, both objects are referenced. However, when obj1 is set to null, it no longer references the object { name: 'John' }. At this point, the garbage collector identifies that there are no remaining references to that object, making it eligible for garbage collection.

Object Lifecycle and Garbage Collection

Understanding an object’s lifecycle helps us grasp when it becomes eligible for garbage collection. JavaScript employs a reachability concept. If an object is reachable through any existing reference, it will not be garbage collected.

Consider the following example:

function createObjects() {
const obj1 = { name: 'John' };
const obj2 = { name: 'Jane' };
return [obj1, obj2];
}

const objects = createObjects();

In this case, the createObjects function creates two objects, obj1 and obj2, which are stored in the heap memory. The array [obj1, obj2] is then returned and assigned to the objects variable. As long as the objects variable holds a reference to the array, the objects will not be eligible for garbage collection.

Automatic Memory Management Benefits

One of the advantages of JavaScript’s garbage collection is automatic memory management. Developers do not need to explicitly deallocate memory; the garbage collector handles it for us. This reduces the chances of memory leaks and simplifies memory management.

Conclusion

By understanding memory allocation and garbage collection in JavaScript, you can write more efficient and reliable code. The heap and stack manage memory allocation, while the garbage collector ensures that memory is released when it’s no longer needed. Appreciating these concepts will empower you to create high-performance JavaScript applications. Embrace the automatic memory management provided by JavaScript, and let the garbage collector handle your code’s cleanliness behind the scenes.

--

--

Ayush Gupta

Software Engineer @Quizizz | Ex Sharechat | Talks about - Designing Scaleable Systems, Cost & Performance Optimisations, Programming Languages, DSA