Understanding Garbage Collection in JavaScript

Memory management in JavaScript is an automatic process that happens behind the scenes. When we create primitives, objects, or functions in JavaScript, they all consume memory. But what happens when these elements are no longer needed? How does the JavaScript engine identify and clean up unnecessary memory?

The Concept of Reachability

At the core of memory management in JavaScript lies the concept of reachability. Reachable values are those that are accessible or usable in some way, and they are guaranteed to be stored in memory.

  1. There are certain values called “roots” that are inherently reachable and cannot be deleted. Examples of roots include the currently executing function, its local variables and parameters, other functions on the current chain of nested calls, their local variables and parameters, and global variables.

  2. Any other value is considered reachable if it can be reached through a reference or a chain of references from a root. For example, if there’s an object in a global variable, and that object has a property referencing another object, both objects are considered reachable.

The JavaScript engine has a garbage collector, a background process that monitors all objects and clears out those that have become unreachable.

A Simple Example

Let’s start with a simple example to understand how garbage collection works. Suppose we have the following code:

let user = {
  name: "John"
};

In this case, the global variable user references the object {name: "John"}. If the value of user is overwritten or the reference is lost, the object becomes unreachable. The garbage collector will then remove it from memory.

See also  A More Intelligent Approach to Mastering Javascript

Two References

Now let’s consider a scenario where we have two references to an object. Suppose we copy the reference from user to admin:

let admin = user;

Even if we overwrite the value of user, the object is still reachable through the admin variable. Therefore, it must stay in memory. However, if we also overwrite admin, the object can be removed by the garbage collector.

Interlinked Objects

Let’s explore a more complex example involving interlinked objects. Consider the following code:

function marry(person1, person2) {
  person1.spouse = person2;
  person2.spouse = person1;
  return {
    married: true,
    husband: person1,
    wife: person2
  }
}

let John = { name: "John" };
let Ann = { name: "Ann" };

let family = marry(John, Ann);

In this case, the marry function “marries” two objects by giving them references to each other. The resulting memory structure looks like this:

John -> spouse: Ann
Ann -> spouse: John
family -> married: true, husband: John, wife: Ann

As long as any object has an incoming reference, it remains reachable. However, if we remove both references, for example:

delete John.spouse;
delete Ann.spouse;

Then, the object John has no incoming references and becomes unreachable. The garbage collector will remove John from memory along with all its data.

Unreachable Island

In some cases, an entire island of interlinked objects can become unreachable and be removed from memory. Consider the following example:

let source = {
  data: "some data"
};

let target = {
  reference: source
};

source = null;
target = null;

Initially, the source object references the target object, which, in turn, references the source object. However, when both references are deleted, the entire island becomes unreachable, and the garbage collector will remove it from memory.

See also  ProgramMatek: Introducing Dynamic Feature Flags for Node.js

Internal Algorithms

The primary garbage collection algorithm used in JavaScript is called “mark-and-sweep.” Here are the steps typically involved in garbage collection:

  1. The garbage collector starts by marking the roots and remembering them.

  2. It then visits and marks all references from the roots.

  3. Next, it visits the marked objects and marks their references. To avoid revisiting the same object multiple times, all visited objects are remembered.

  4. This process continues until every reachable reference from the roots is visited.

  5. Finally, all objects that were not marked as reachable are removed.

Modern JavaScript engines incorporate various optimizations to make garbage collection faster and minimize any impact on code execution:

  • Generational collection: Objects are divided into two sets: “new” and “old.” New objects that have a short lifespan can be cleared from memory more frequently. Old objects, which survive for longer periods, are examined less often.

  • Incremental collection: Instead of performing one extensive garbage collection, the engine divides the object set into multiple parts and clears them one by one. This approach minimizes execution delays caused by garbage collection.

  • Idle-time collection: The garbage collector tries to run only when the CPU is idle to reduce potential execution disruptions.

These optimizations improve the overall performance of garbage collection. While there are other optimizations and variations of garbage collection algorithms, diving into those specifics is beyond the scope of this article.

Summary

To recap, some key points to remember about garbage collection in JavaScript are:

  • Garbage collection occurs automatically and cannot be forced or prevented.
  • Objects remain in memory as long as they are reachable.
  • Being referenced is not the same as being reachable. A group of interlinked objects can become unreachable as a whole.
  • Modern JavaScript engines employ advanced garbage collection algorithms to optimize memory management.
See also  How to Build Mobile Apps with JavaScript

For further exploration, you may find the book “The Garbage Collection Handbook: The Art of Automatic Memory Management” by R. Jones et al informative. Additionally, if you have a background in low-level programming, you can delve into more detailed information about the garbage collector in V8 by reading the article “A tour of V8: Garbage Collection” and exploring the V8 blog’s articles on memory management. Keep in mind that garbage collection techniques may differ across different JavaScript engines, and it’s essential to stay up-to-date with the latest developments and engine-specific optimizations if low-level optimizations are your focus.

For more information about ProgramMatek’s offerings and services, visit ProgramMatek.