preface

The program needs memory to run. The operating system or runtime must supply memory whenever a program requests it. A memory leak is simply memory that is no longer used and is not released in time. To better avoid memory leaks, let’s first introduce the Javascript garbage collection mechanism.

In languages such as C and C++, developers can directly control the application and reclamation of memory. But in Java, C#, JavaScript language, variable memory space application and release are handled by the program itself, developers do not need to care about. That is, Javascript has Garbage Collecation.

Check out the GitHub blog for more great articles. 50 great articles a year!

First, the necessity of garbage collection

The following quote is from the Authoritative Guide to JavaScript, 4th edition

Since strings, objects, and arrays have no fixed size, they can only be dynamically allocated when their size is known. Every time a JavaScript program creates a string, array, or object, the interpreter must allocate memory to store that entity. Whenever memory is dynamically allocated like this, it eventually has to be freed so that it can be reused. Otherwise, the JavaScript interpreter will use up all the available memory in the system, causing the system to crash.

Unlike C/C++, JavaScript has its own garbage collection mechanism.

The mechanism of JavaScript garbage collection is simple: find variables that are no longer used, and then free the memory they occupy, but this process is not always done, because it is expensive, so the garbage collector executes periodically at fixed intervals.

var a = "Boat in the waves.";
var b = "Front End Craftsman"; var a = b; / / rewrite aCopy the code

When this code runs, the string “Boat in the Wave” loses its reference (it was previously referenced by A), and when the system detects this fact, it frees up the string’s storage so that it can be reused.

Second, garbage collection mechanism

How does garbage collection know which memory is no longer needed?

There are two methods of garbage collection: tag cleanup and reference counting. Reference counting is less common and tag clearing is more common.

1. Clear the mark

This is the most commonly used garbage collection method in javascript. When a variable enters the execution environment, it is marked as “entering the environment”. Logically, the memory occupied by variables entering the environment can never be freed, because they are likely to be used as soon as the execution flow enters the appropriate environment. When a variable leaves the environment, it is marked “out of the environment.”

When the garbage collector runs, it marks all variables stored in memory. It then removes the markup of variables in the environment and those referenced by variables in the environment. Variables tagged after that are considered to be ready to be deleted because they are no longer accessible to variables in the environment. At last. The garbage collector does the cleanup, destroying the tagged values and reclaiming the memory space they occupy.

Let’s use an example to explain this method:

Var m = 0,n = 19 Add (m, n) // Mark a, b, c as entering the environment. Console.log (n) // A,b, and c are marked as leaving the environment and waiting for garbage collection.function add(a, b) {
  a++
  var c = a + b
  return c
}
Copy the code

2. Reference counting

“Reference counting” means that the language engine has a “reference table” that holds the number of references to all resources (usually values) in memory. If the number of references to a value is zero, the value is no longer used, so the memory can be freed.

If a value is no longer needed, but the reference number is not zero, the garbage collection mechanism cannot free the memory, resulting in a memory leak.

var arr = [1, 2, 3, 4];
arr = [2, 4, 5]
console.log('Boat in the Waves');
Copy the code

In the above code, the array [1, 2, 3, 4] is a value that consumes memory. The variable arr is the only reference to this value, so the number of references is 1. Although the following code does not use ARR, it continues to consume memory. How to free memory is described below.

In the third line of code, the variable arr referenced by the array [1, 2, 3, 4] gets another value, so the number of references to the array [1, 2, 3, 4] is reduced by one, and the number of references to the array [1, 2, 3, 4] becomes zero.

But there’s one big problem with reference counting: circular references

function func() {
    let obj1 = {};
    letobj2 = {}; obj1.a = obj2; // obj1 references obj2. Obj2. a = obj1; // obj2 references obj1}Copy the code

When func is finished, it returns undefined, so the entire function and its internal variables should be reclaimed, but neither obj1 nor obj2 has a zero reference count, so they won’t be reclaimed.

To solve the problem of circular references, it is best to manually set them to null when they are not in use. The above example can be done as follows:

obj1 = null;
obj2 = null;
Copy the code

Three, what will cause a memory leak?

While JavaScript does automatically garbage collect, if we write our code incorrectly, variables will remain “in the environment” and cannot be collected. Here are some common cases of memory leaks:

1. Unexpected global variables

function foo(arg) {
    bar = "this is a hidden global variable";
}
Copy the code

Bar is not declared and will become a global variable that will not be released until the page closes.

Another unexpected global variable can be created by this:

function foo() {
    this.variable = "potential accidental global"; } // foo calls itself. This points to the global object (window) foo();Copy the code

This error can be avoided by adding ‘use strict’ to the header of a JavaScript file. Enable strict mode parsing JavaScript to avoid unexpected global variables.

2. Forgotten timers or callbacks

var someResource = getData();
setInterval(function() {
    var node = document.getElementById('Node');
    if(node) {// process node and someResource node.innerhtml = json.stringify (someResource)); }}, 1000);Copy the code

If the element with the id of Node is removed from the DOM, the timer will still exist, and because the callback contains a reference to someResource, the someResource outside the timer will not be released.

3. The closure

function bindEvent(){
  var obj=document.createElement('xxx')
  obj.onclick=function(){
    // Even if it is a empty function}}Copy the code

Closures can maintain local variables within a function so that they cannot be released. When the above example defined the event callback, the closure was formed because the function was defined within the function, and the internal function — the event callback — referred to the external function.

// Define the event handler on the outsidefunction bindEvent() {
  var obj = document.createElement('xxx') obj.onclick = onclickHandler} // or remove references to the DOM in the external function that defines the event handlerfunction bindEvent() {
  var obj = document.createElement('xxx')
  obj.onclick = function() {
    // Even if it is a empty function
  }
  obj = null
}
Copy the code

The solution is to define the event handler externally, unwrap the closure, or remove references to the DOM in the external function that defines the event handler.

4. Uncleaned DOM element references

Sometimes it’s useful to save the internal data structures of DOM nodes. If you want to quickly update a few rows of a table, it makes sense to store each row of the DOM as a dictionary (JSON key-value pairs) or an array. At this point, there are two references to the same DOM element: one in the DOM tree and one in the dictionary. When you decide to delete these lines in the future, you need to remove both references.

var elements = {
    button: document.getElementById('button'),
    image: document.getElementById('image'),
    text: document.getElementById('text')};function doStuff() {
    image.src = 'http://some.url/image';
    button.click();
    console.log(text.innerHTML);
}
function removeButton() {
    document.body.removeChild(document.getElementById('button')); // In this case, there is still a global one# button reference// Elements dictionary. The button element is still in memory and cannot be reclaimed by GC. }Copy the code

Although we removeChild the button, we still have a reference to #button in the Elements object. In other words, the DOM element is still in memory.

Four, memory leak identification method

New versions of Chrome are viewed in Performance:

  • Open the developer tool Performance
  • Check Screenshots and Memory
  • The dot in the upper left corner starts recording (record)
  • To stop recording

In the figure, we can see that there are periodic falls in memory and we can see the cycle of garbage collection. If min is rising at the lowest value after garbage collection (which we call min), then we must have a serious memory leak problem.

Some ways to avoid memory leaks:

  • Reduce unnecessary global variables, or long – lived objects, and garbage collection of useless data in time
  • Pay attention to the program logic to avoid “infinite loops” and the like
  • Avoid creating too many objects

The bottom line is that there is a rule to follow: if you don’t use anything, return it on time

Fifth, garbage collection use scenario optimization

1. Optimize arrays

Assigning [] to an array object is a shortcut to clearing an array (e.g., arr = [];). “, but note that this creates a new empty object and turns the original array object into a small piece of memory garbage! In fact, setting the length of the array to 0 (arr.length = 0) can also clear the array and reuse the array, reducing memory waste.

const arr = [1, 2, 3, 4];
console.log('Boat in the Waves'); Arr. length = 0 // The number can be emptied directly, and the array type is not changed. // arr = []; We made the a variable an empty array, but we reapplied an empty array object on the heap.Copy the code

2. Reuse objects as much as possible

Reuse objects as much as possible, especially in places like loops create new objects, reuse them if you can. Unused objects are set to null as soon as possible and garbage collected as soon as possible.

Var t = {} // Each loop creates a new object.for(var i = 0; i < 10; i++) { // var t = {}; // Each loop creates a new object. t.age = 19 t.name ='123'T.index = I console.log(t)} t = null; Wait for garbage collection.Copy the code

3. Function expressions within a loop that can be reused are best placed outside the loop.

// It is also best not to use function expressions in loops.for (var k = 0; k < 10; k++) {
  var t = function(a) {// Create the function object 10 times. console.log(a) } t(k) }Copy the code
// Recommended usagefunction t(a) {
  console.log(a)
}
for (var k = 0; k < 10; k++) {
  t(k)
}
t = null
Copy the code

I recommend a good BUG monitoring toolFundebug, welcome to the free trial!

Welcome to pay attention to the public number: front-end craftsman, your growth we witness together!

The resources

  • Everest Architecture (highly recommended)
  • JavaScript Advanced Insider (JS Advanced)
  • JavaScript garbage collection mechanism
  • JavaScript memory leaks tutorial
  • The Definitive Guide to JavaScript (4th edition)
  • Garbage collection in JavaScript
  • JavaScript Memory Leaks and How to Avoid them