1
头图
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"


来了老弟
508 声望31 粉丝

纸上得来终觉浅,绝知此事要躬行


« 上一篇
JavaScript闭包
下一篇 »
高阶函数