Introduction

As a web developer, did you know that every line of code you write has an impact on the performance of your application? When it comes to JavaScript, one of the most important areas to focus on is memory management.

Think about it: every time a user interacts with your site, they create new objects, variables, and functions. If you’re not careful, these objects can pile up, clogging up your browser’s memory and degrading the overall user experience. It’s like a traffic jam on the information superhighway, a frustrating bottleneck that can turn users off.

But it doesn’t have to be that way. With the right knowledge and techniques, you can take control of your JavaScript memory and ensure that your application runs smoothly and efficiently.

In this post, we’ll explore the ins and outs of JavaScript memory management, including the common causes of memory leaks and strategies to avoid them. Whether you’re a professional or novice JavaScript developer, you’ll gain a deeper understanding of how to write lean, mean, and fast code.

Learn JavaScript memory management

1. Garbage Collector(GC)

The JavaScript engine uses GC to release memory that is no longer used.The work of GC is an object that the application is no longer used to identify and delete the application.It is implemented by continuously monitoring objects and variables in the code, and which objects and variables are still being referenced to achieve this.Once an object is no longer used, GC marked it to delete and release the memory it is using.

GC uses a technology called “mark and removal” to manage memory.It first labels all the objects that are still in use, and then “sweep” and delete all unsigned objects.This process will be carried out on a regular basis and will be carried out when the heap memory is insufficient to ensure that the memory of the application is always as efficient as possible.

2. Stacks and heaps

When it comes to memory in JavaScript, there are two main participants: stacks and heaps.

Stack is used to store data required only during the function execution.It is fast and efficient, but the capacity is limited.When a function is called, the JavaScript engine presses the variables and parameters of the function into the stack. When the function returns, the JavaScript engine pops up again.The stack is used for fast access and fast memory management.

On the other hand, the data required for the entire life cycle is used for storing applications.It is a little slower than the stack, the organizationality is a little bit, but the capacity is much larger.It is used to storage objects, arrays, and other complex data structures that need to be accessed multiple times.

Common causes of memory leakage

You know that the memory leak may be like a sneaky enemy, which will sneak into your application and cause performance problems.By understanding the common causes of memory leaks, you can armed yourself with the knowledge you need.

1. Cyclic reference

One of the most common reasons for memory leakage is cycle reference.When two or more objects are cited, this happens, thereby forming a cycle where the GC cannot destroy.This may cause objects to remain in memory for a long time after no longer need.

Here is an example:

1
2
3
4
5
6
7
8
9
10
11
12
13
let object1 = {};
let object2 = {};

// create a circular reference between object1 and object2
object1.next = object2;
object2.prev = object1;

// do something with object1 and object2
// ...

// set object1 and object2 to null to break the circular reference
object1 = null;
object2 = null;

In this example, we created two objects, object1 and object2, and let them create cyclic references between them by adding Next and Prev properties.

Then, we set object1 and object2 to null to break the cycle reference, but because the GC cannot break the cycle reference, the object will be kept in memory for a long time after no longer need, which will cause memory leakage.

In order to avoid this type of memory leakage, we can use a technology called “manual memory management”, and to delete the attributes of creating cycle reference by using the Delete keywords of JavaScript.

1
2
delete object1.next;
delete object2.prev;

Another way to avoid such memory leaks is to use Weakmap and Weakset, which allows you to create weak references to objects and variables.

2.Event monitor

Another common reason for memory leakage is event monitoring. When you attach an event listener to the element, it will create a reference to the listener function, which can prevent the memory of the use of the garbage GC.If the listener function is not deleted when the element is no longer needed, this may cause memory leakage.

Let’s take a look at an example:

1
2
3
4
5
6
7
8
9
10
11
12
let button = document.getElementById("my-button");

// attach an event listener to the button
button.addEventListener("click", function() {
console.log("Button was clicked!");
});

// do something with the button
// ...

// remove the button from the DOM
button.parentNode.removeChild(button);

In this example, we attach the event listener to the button element, and then delete the button from the DOM.Even if the button element no longer exists in the document, the event listener is still attached to it, which will create a reference to the listener function to prevent GC from release the memory used by the element.If the listener function is not deleted when the element is no longer needed, it may cause memory leakage.

In order to avoid such memory leaks, it is important to delete an event listener when you no longer need this element:

1
2
3
button.removeEventListener("click", function() {
console.log("Button was clicked!");
});

Another method is to use the EventTarget.removeAllListens () method to delete all event listeners that have been added to specific event goals.

1
button.removeAllListeners();

3.Global variable

The third common cause of memory leakage is global variables.When you create a global variable, you can access it from any position in the code, which makes it difficult to determine when it is no longer needed.This may cause variables to remain in memory for a long time after no longer need.This is an example:

1
2
3
4
5
6
7
8
9
10
11
// create a global variable
let myData = {
largeArray: new Array(1000000).fill("some data"),
id: 1
};

// do something with myData
// ...

// set myData to null to break the reference
myData = null;

In this example, we created a global variable myData and stored a lot of data in it.

Then we set myData to null to reference it, but because the variable is a global variable, it can still be accessed from any position in your code, and it is difficult to determine when it is no longer needed, which will leadThis variable retains in memory for a long time. After no longer it is needed, the memory leaks.

To avoid this type of memory leakage, you can use the “functional field” technology.It involves creating a function and declared variables in this function so that they can only access within the function range.In this way, when the function is no longer needed, the variable is automatically recovered by garbage.

1
2
3
4
5
6
7
8
9
10
function myFunction() {
let myData = {
largeArray: new Array(1000000).fill("some data"),
id: 1
};

// do something with myData
// ...
}
myFunction();

Another method is to use the let and const of JavaScript instead of var, which allows you to create variables in blocks.The variables declared with let and const can only be accessed in their blocks, and they will be automatically collected by GC when they exceed the range.

1
2
3
4
5
6
7
8
9
{
let myData = {
largeArray: new Array(1000000).fill("some data"),
id: 1
};

// do something with myData
// ...
}

The best practice of manual memory management

JavaScript provides memory management tools and technology to help you control the memory usage of the application.

1.Use weak reference

One of the most powerful memory management tools in JavaScript is Weakmap and Weakset.These are special data structures that allow you to create weak references to objects and variables.

Weak references are different from conventional references because they do not prevent the memory used by GC release objects.This makes them a good tool for avoiding memory leaks caused by cycle reference.This is an example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let object1 = {};
let object2 = {};

// create a WeakMap
let weakMap = new WeakMap();

// create a circular reference by adding object1 to the WeakMap
// and then adding the WeakMap to object1
weakMap.set(object1, "some data");
object1.weakMap = weakMap;

// create a WeakSet and add object2 to it
let weakSet = new WeakSet();
weakSet.add(object2);

// in this case, the garbage collector will be able to free up the memory
// used by object1 and object2, since the references to them are weak

In this example, we created two objects, object1 and object2, and created cyclic references between them by adding them to Weakmap and Weakset.

Because the reference to these objects is weak, GC will be able to release the memory they use, even if they are still being cited.This helps to prevent memory leaks caused by cycle reference.

2. Use GC API

Another memory management technology is to use GC API, which allows you to manually trigger garbage collection and obtain information about the current state of the stack.

This is very useful for debugging memory leakage and performance problems.

The following is an example:

1
2
3
4
5
6
7
8
9
let object1 = {};
let object2 = {};

// create a circular reference between object1 and object2
object1.next = object2;
object2.prev = object1;

// manually trigger garbage collection
gc();

In this example, we created two objects, object1 and object2, and created a cycle reference between them by adding the next and prev properties to them.We then use the gc() function to manually trigger the garbage collection, which will release the memory used by the object, even if they are still referenced.

Be sure to note that not all JavaScript engines support the gc() function, and its behavior may vary from the engine.It should also be noted that manual triggering garbage recycling will affect performance. Therefore, it is recommended to use it with caution and use it only when necessary.

In addition to the gc() function, JavaScript also provides the global.gc() function for some JavaScript engines, and also provides performance.gc() for some browser engines. They can be used to check the current state of the pile and measure the garbage collection process.performance.

3. Use the pile of snapshot and analyzer

JavaScript also offers a pile of snapshots and analyzers to help you understand how your application uses memory.The pile fast photo allows you to take a snapshot of the current state and analyze it to see which objects are used most.

The following is an example that explains how to use the pile snapshot to identify the memory leakage in the application:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Start a heap snapshot
let snapshot1 = performance.heapSnapshot();

// Do some actions that might cause memory leaks
for (let i = 0; i < 100000; i++) {
myArray.push({
largeData: new Array(1000000).fill("some data"),
id: i
});
}

// Take another heap snapshot
let snapshot2 = performance.heapSnapshot();

// Compare the two snapshots to see which objects were created
let diff = snapshot2.compare(snapshot1);

// Analyze the diff to see which objects are using the most memory
diff.forEach(function(item) {
if (item.size > 1000000) {
console.log(item.name);
}
});

In this example, we took two stacked snapshots before and after and after the cycle of the array was executed, and then compared these two snapshots to identify the object created during the cycle.

Then, we can analyze the differences to see which objects use the most memory, which can help us identify memory leaks caused by big data.

The analyzer allows you to track the performance of the application and identify the area with high memory usage:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let profiler = new Profiler();

profiler.start();

// do some actions that might cause memory leaks
for (let i = 0; i < 100000; i++) {
myArray.push({
largeData: new Array(1000000).fill("some data"),
id: i
});
}

profiler.stop();

let report = profiler.report();

// analyze the report to identify areas where memory usage is high
for (let func of report) {
if (func.memory > 1000000) {
console.log(func.name);
}
}

In this example, we use the JavaScript analyzer to start and stop tracking the performance of our application.The report will show information about the calls that have been called and the memory usage of each function.

Not all JavaScript engines and browsers support the stacking and analyzers, so it is important to use them to check compatibility in your application.

In conclusion

We have introduced the basic knowledge of JavaScript memory management, including garbage recovery processes, different types of memory, and memory management tools and technologies available in JavaScript.We also discussed the common causes of memory leaks and provided examples of how to avoid them.

By spending time understanding and implementing the best practice of these memory management, you will be able to create applications that eliminate the possibility of memory leakage.