Memory Management in JavaScript – Complete guide
There are many programming languages out there, each having its own characteristics. An essential characteristic of any programming language is how memory efficient it is. The speed of a language depends on how well it handles memory and optimizes at runtime. While memory management differs from programming language, they share common concepts such as Stack, Heap, etc.
In this article, we’ll discuss how memory management works in JavaScript. This article will teach you how JavaScript stores, uses, and free up variables and objects. We’ll also discuss in brief about garbage collection and its core algorithm.
Memory life cycle in JavaScript
The memory life cycle defines a JavaScript variable or object’s journey from initialization to removal. JavaScript and all the other programming languages have a similar memory life cycle, as depicted below.
Initialization is the creation of the variable. In JavaScript, we do this using the let
, const
, or var
keywords. During this stage, the JavaScript Engine reserves memory for the variables and stores the values inside the allocated memory space.
let name = 'codedamn';
Code language: JavaScript (javascript)
Accessing is using the variable of the object inside the code. Many times, we’ll also be modifying the values of these variables or objects during this stage.
if (name === 'codedamn') {
// Changing the value
name = 'Codedamn!';
}
Code language: JavaScript (javascript)
Removal is the deallocation of the reserved memory taken up by the variables. After deallocation, we won’t be able to access the variable from our JavaScript code. For example, we cannot access the variables created inside a function.
function sum() {
let x = 10;
let y = 20;
let sum = x + y;
}
console.log(x); // This will throw and error!
Code language: JavaScript (javascript)
How does JavaScript store variables?
We talked about allocation and deallocation, but the question is that where do these allocations and deallocation happen?
JavaScript consists of two types of memory spaces, i.e., Stack and Heap, to store variables. The difference between them is what variables get stored in which type of storage. Let’s take a closer look at stacks and heaps.
Stack
You’ve probably heard of a stack if you’ve studied Data Structures. Consider a stack of plates; you can only remove the topmost plate. To remove the last plate, you must first remove all the plates above it. That’s all a stack is.
Let’s say you have some variables that you add one by one to the stack. The last variable pushed is at the top, which means it will be the first to get removed when the memory gets deallocated.
JavaScript uses the stack data structure to store static or fixed-size data. This includes all numbers, strings, booleans, and other primitive data types. These data types have a fixed size known at compile time. Variables such as objects, arrays, etc., are not stored in the stack as their size varies during run time.
Heap
Since Stack can only store fixed-size or static data, where does JavaScript store dynamic data? The answer is Heap. The heap memory, unlike stack memory, doesn’t have a fixed-size limitation, i.e., the memory gets allocated dynamically.
JavaScript uses a heap for storing variables whose size is unknown at compile time or may vary at the run time, such as objects, arrays, functions, etc.
The JavaScript Engine dynamically allocates memory to the heap. Initially, the heap size depends on available system memory, and it can be dynamically increased/decreased based on the need. You can check the current memory usage of your Node.js application using the process.memoryUsage()
method. Here is an example:
This method shows the total allocated memory, the allocated heap memory, the used heap memory, and the external memory usage. It’s quite useful when trying to monitor your application’s memory usage.
You may manually adjust the heap size of your Node.js process by simply passing the
--max_old_space_size
flag using the command line.
Values and references
JavaScript allocates memory for objects within the heap, but we must have a reference to that memory location to access the value. The reference to the memory location resides in the stack memory.
Since the variable name contains a reference to the heap object, whenever we refer to the variable in our code, it’s the reference and not the actual object. Let’s take an example:
let box1 = {
width: 100,
height: 50
};
let box2 = box1;
box2.width = 200;
console.log('Box 1 width: ', box1.width);
console.log('Box 2 width: ', box2.width);
Code language: JavaScript (javascript)
Test it out quickly using Codedamn’s JavaScript Playground embedded below (Toggle the DevTools on the preview window to see the logs):
You can see that changing the width
property of the box2
object will also change the width
property of the box1
object. This happens because while assigning the value of box1
into box2
, we are actually copying the reference and not the actual object. Therefore, both box1
and box2
point to the same object in the. As both point to the same object, changing one changes the other.
Garbage collection
In languages like C, C++, etc., it’s our job to allocate memory and safely deallocate it after use. Therefore, if the memory is not freed up after use, the program will run with an unused allocated memory block. We term this condition a memory leak. Therefore, to prevent such conditions, modern programming languages, like JavaScript, come with a Garbage collector.
A Garbage collector is a part of the JavaScript Engine whose job is to free up unused memory from the heap using a garbage collection process. It ensures proper memory management while the application is running. Therefore, the programmer doesn’t have to worry about manual memory management, the garbage collector does it.
It seems pretty straightforward, right? Well, No. There are a lot of challenges to achieving this. There isn’t any algorithm that guarantees that it frees up all the unused memory at the current moment. But, there are algorithms such as the mark and sweep, that perform pretty well and come close to removing most of the unwanted pieces of memory from the heap.
Mark and sweep
It’s one of the most common garbage collection algorithms out there. Basically, it marks the objects not reachable by the root object, i.e., the window
object as trash or garbage. Later, the garbage collector removes these marked objects from the heap. Have a look at the example below.
In the above example, the employee
object is reachable from the window
object. Now, because the company
and person
objects are reachable through the employee
object, they are ultimately reachable by the window object. But, the car
object and other objects connected to it are not accessible through the window
object. Therefore, the algorithm marks these objects and later free up the memory used by them.
One tradeoff though is the global variables declared using var
. When declaring a variable with var
, the JavaScript Engine attaches them to the window
object. Now, since these variables are always reachable by window
objects, there are never removed by the garbage collector.
Summary
We looked at how memory management works in JavaScript in this post. It stores data in two memory spaces, the Stack and the Heap. The stack is used to store static data, whereas the heap is used to store dynamic data. The stack contains references to the objects in a heap. JavaScript also uses a garbage collector to deallocate unused memory from the heap. The mark and sweep technique is one of the most common garbage collection algorithms.
You can test out all the concepts we talked about using Codedamn Playground.
This article hopefully provided you with some new information. Share it with your friends if you enjoy it. Also, please provide your feedback in the comments section.
Thank you so much for reading 😄
Sharing is caring
Did you like what Varun Tiwari wrote? Thank them for their work by sharing it on social media.
No comments so far
Curious about this topic? Continue your journey with these coding courses: