Memory management doesn't need to be considered too much in traditional web development, and usually doesn't have serious consequences. Because when the user clicks a link to open a new page or refreshes the page, the information in the page is cleared from memory.
With the increase of SPA (Single Page Application) applications, we need to pay more attention to memory when coding. Because if the memory used by the application gradually increases, it will directly affect the performance of the web page, and even cause the browser tab to crash.
In this article, we'll examine scenarios where JavaScript coding causes memory leaks and provide some memory management advice.
What is a memory leak?
We know that browsers store objects in heap memory, and they can be accessed through the index chain. GC (Garbage Collector) is a background process of the JavaScript engine, which can identify which objects are in a useless state, remove them, and release the occupied memory.
If a variable that should be reclaimed by GC is indexed by other objects and can be accessed through root, this means that there is redundant memory occupation in the memory, which will lead to the performance degradation of the application, and then a memory leak occurs. .
How to find memory leaks?
Memory leaks are generally difficult to detect and locate, and the browser's built-in tools can help us analyze whether there are memory leaks and the causes of memory leaks.
developer tools
Open the Developer Tools - Performance tab to analyze the visual data of the current page. Both Chrome and Firefox have excellent memory analysis tools that provide developers with memory allocation by analyzing snapshots.
Common situations where JS causes memory leaks
Unnoticed global variables
Global variables can be accessed by root and will not be recycled by GC. Some local variables in non-strict mode may become global variables, causing memory leaks.
- Assign value to undeclared variable
- this points to the global object
function createGlobalVariables() {
leaking1 = 'I leak into the global scope'; // assigning value to the undeclared variable
this.leaking2 = 'I also leak into the global scope'; // 'this' points to the global object
};
createGlobalVariables();
window.leaking1; // 'I leak into the global scope'
window.leaking2; // 'I also leak into the global scope'
How to avoid it? Use strict mode.
Closure
After the closure function is executed, the variables in the scope will not be recycled, which may lead to memory leaks:
function outer() {
const potentiallyHugeArray = [];
return function inner() {
potentiallyHugeArray.push('Hello'); // function inner is closed over the potentiallyHugeArray variable
console.log('Hello');
};
};
const sayHello = outer(); // contains definition of the function inner
function repeat(fn, num) {
for (let i = 0; i < num; i++){
fn();
}
}
repeat(sayHello, 10); // each sayHello call pushes another 'Hello' to the potentiallyHugeArray
// now imagine repeat(sayHello, 100000)
timer
Use setTimeout or setInterval:
function setCallback() {
const data = {
counter: 0,
hugeString: new Array(100000).join('x')
};
return function cb() {
data.counter++; // data object is now part of the callback's scope
console.log(data.counter);
}
}
setInterval(setCallback(), 1000); // how do we stop it?
Only when the timer is cleaned up, the data inside its callback function will be cleaned up from memory, otherwise it will be retained until the application exits.
How to avoid it?
function setCallback() {
// 'unpacking' the data object
let counter = 0;
const hugeString = new Array(100000).join('x'); // gets removed when the setCallback returns
return function cb() {
counter++; // only counter is part of the callback's scope
console.log(counter);
}
}
const timerId = setInterval(setCallback(), 1000); // saving the interval ID
// doing something ...
clearInterval(timerId); // stopping the timer i.e. if button pressed
The timer is assigned to timerId and manually cleared using clearInterval(timerId).
Event listeners
addEventListener will also remain in memory and cannot be recycled until we use removeEventListener, or the DOM that adds the listener event is removed.
const hugeString = new Array(100000).join('x');
document.addEventListener('keyup', function() { // anonymous inline function - can't remove it
doSomething(hugeString); // hugeString is now forever kept in the callback's scope
});
How to avoid it?
function listener() {
doSomething(hugeString);
}
document.addEventListener('keyup', listener); // named function can be referenced here...
document.removeEventListener('keyup', listener); // ...and here
// 或者
document.addEventListener('keyup', function listener() {
doSomething(hugeString);
}, {once: true}); // listener will be removed after running once
JS memory leak summary
Identifying and fixing JS memory usage issues is a challenging task, and avoiding memory leaks should be a priority during coding. Because it is related to the performance and user experience of the application.
This article is translated from "Causes of Memory Leaks in JavaScript and How to Avoid Them"
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。