Understanding Memory Management in Javascript

Understanding Memory Management in Javascript

A God-like skills for all kinds of developers

·

6 min read

Prologue

Once upon a time in the faraway land of C++ lived two evil twins called malloc() and alloc(). The evil twins controlled every variable and function that wanted to enter certain parts of the city called programs.

Slowly and steadily their influence and evil eye increased and started causing issues like Memory Leaks and Garbage data in almost all programs. The citizens had no choice but to relocate to other realms such as Java, Javascript, and others.

In this article, we will explore how these evils can be avoided in our very own mystic realm of Javascript.

Introduction

Continuing onto our ultimate quest for World Domination using Javascript, we're here seeking the ultimate Super Saiyan power, i.e., effective Memory Management in Javascript.

Memory is a limited resource (just like time, we think we have unlimited time, but in fact, it is very limited). Thus, it is important to spend memory wisely and save as much memory as possible in our programs.

This ultimately helps us avoid issues like Memory Leaks, increased resource consumption, etc.

What is Memory Management

Memory management refers to the process of allocating, utilizing, and releasing memory resources when no longer needed. It ensures efficient utilization of memory and prevents memory leaks or excessive memory usage.

JS allocates memory for variables and objects dynamically, meaning that it assigns memory to them at runtime rather than at compile time. When a variable or object is created, JS determines the amount of memory it needs and assigns it accordingly. This process is known as dynamic memory allocation.

Memory Life Cycle

In JS, memory management involves the management of two critical components: the Stack and the Heap.

The Stack

The Stack is a region of memory used for storing static data such as function calls, local variables, and function scope. It operates on the Last-In-First-Out (LIFO) principle. This memory is managed by the JS engine and developers have no control over it.

The Heap

The Heap is a region of memory used for dynamic memory allocation. It is where objects, closures, and large data structures reside.

Here, the memory allocation and deallocation in the Heap are more complex.

Memory Allocation

In JavaScript, variables can be assigned primitive values (like numbers and booleans) or reference values (like objects and arrays). When a variable is assigned a primitive value, it is stored directly in the Stack. However, when a variable is assigned a reference value, the reference is stored in the Stack, while the actual object resides in the Heap.

Consider the below-given example:-

let x = 42; // Primitive value stored in the Stack
let obj = { name: 'John Wick' }; // Reference stored in the Stack, object stored in the Heap

Garbage Collection

Garbage Collection is the process of automatically reclaiming memory that is no longer in use by the program. JavaScript employs a garbage collector to free up memory occupied by objects that are no longer reachable.

Garbage collection involves identifying objects that are no longer needed and releasing their memory. JavaScript’s garbage collector works by traversing the object graph and marking reachable objects. Unreachable objects are then removed in the next step using algorithms such as Reference Counting, Mark and Sweep algorithm and, Generational Garbage Collection.

Reference Counting

The reference counting garbage collection algorithm works by keeping track of the number of references to an object. When an object no longer has any references, it is considered eligible for garbage collection. While this algorithm is simple and efficient, it has some limitations. For example, it cannot detect circular references.

Mark and Sweep Algorithm

Mark and Sweep algorithm works in two phases:

  • Marking Phase - During the Mark phase, the garbage collector traverses the object graph, starting from the root objects (global object, execution contexts, etc.) and traversing the object graph while marking (or tagging) reachable objects.

    Any object that is not marked during this phase is considered unreachable and is eligible for garbage collection.

  • Sweeping Phase - In the Sweep phase, the garbage collector sweeps out all the non-reachable objects to free up the space. This free space can later be used for new memory allocation.

    In this phase, one may see performance issues if there are a large number of objects to be swept.

This algorithm is more sophisticated than reference counting and can handle circular references, however, it is also slower and requires more memory.

Generational Garbage Collection

Modern JS engines also employ a technique called Generational Garbage Collection. This approach divides objects into different generations based on their age. Frequently accessed objects are moved to younger generations, while less frequently used objects are promoted to older generations. This optimization reduces the garbage collection overhead and improves performance.

Memory Leaks and Performance Issues

Memory leaks can occur in JavaScript when objects are unintentionally kept in memory, even though they are no longer needed.

Causes of Memory Leaks:

Memory leaks can happen due to circular references, unclosed resources, global variables, unremoved event listeners, and improper use of closures. These issues can lead to excessive memory consumption and degrade application performance.

Identifying and Fixing Memory Leaks

To identify memory leaks, tools like Chrome DevTools Memory tab and memory profilers can be used. To fix memory leaks, ensure that resources are properly released, event listeners are removed when no longer needed, and variables are appropriately scoped.

Best Practices for Memory Management

To optimize memory management in JavaScript, follow these best practices:

  • Avoid Global Variables:

    Global variables remain in memory throughout the application’s lifecycle. Minimize the use of global variables and prefer encapsulating variables within functions or modules.

  • Properly Dispose of Event Listeners:

    When adding event listeners, ensure they are properly removed when no longer needed. Otherwise, they can create memory leaks by holding references to objects that should be garbage collected.

  • Reducing object size:

    Large objects can consume a lot of memory, which can be especially problematic in large-scale applications. To reduce object size, consider using primitive types instead of objects, using arrays instead of objects, and minimizing the use of nested objects. Additionally, consider using immutable objects where possible, as they can help reduce memory usage and improve performance.

  • Use Object Pooling:

    For frequently created and destroyed objects, consider implementing object pooling. Object pooling reuses existing objects instead of creating new ones, reducing memory allocation and garbage collection overhead. Let’s see an optimized code example-

Advanced Memory Management Techniques

JavaScript provides advanced memory management techniques to handle specific scenarios.

WeakMap and WeakSet

WeakMap and WeakSet are special data structures in JavaScript that allow objects to be weakly referenced. They can be useful for scenarios where objects need to be garbage collected when they are no longer referenced elsewhere.

Memory Management in Frameworks

Frameworks like React and Angular handle memory management for components. Understanding the memory management strategies employed by these frameworks can help optimize the usage of components and prevent memory leaks.

Conclusion and Next Steps

In this article, we’ve explored the intricate world of Memory Management and Garbage Collection in JavaScript. Understanding how memory is managed and released is essential for writing efficient and performant code. By following best practices, being aware of memory leaks, and leveraging advanced memory management techniques, you can optimize memory usage in your JavaScript applications. Keep exploring, keep learning, and happy coding!

References

Did you find this article valuable?

Support Utkarsh by becoming a sponsor. Any amount is appreciated!