web123456

Common front-end memory leaks and how to avoid them

4 Common Memory Leaks

1. Unexpected global variables
Global variables are hard to reclaim by the garbage collector.

  • undeclared variable

    When we assign a value to a variable in a function but do not declare it:

function fn(){
	a = "aaaaa";
}
  • 1
  • 2
  • 3

At this point, the variable a is equivalent to the variable under the window object

  • Variables created using this
function fn(){
	this.a = "aaaaa";
}
  • 1
  • 2
  • 3
'
(of a computer) run

Here, this points to the window, so the a variable created at this point will also be mounted under the window object

Solutions to avoid this situation

  • Avoid setting global variables
  • Adding 'use strict' to the header of a JavaScript file or to the top of a function enables strict mode, which makes this point to undefined, thus avoiding the need for
  • When you must use a global variable to store large amounts of data, make sure to set it to null or redefine it when you're done.

2. Forgotten timers or callback functions

  • Using timers in your code may also cause memory leaks:
var serverData = loadData()
setInterval(function() {
	var renderer = document.getElementById('renderer')
	if(renderer) {
		renderer.innerHTML = JSON.stringify(serverData)
	}
}, 5000)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

The node renderer references serverData. The timer still points to the node renderer or to the data when it is no longer needed. So even when the renderer node is removed, the interval is still alive and the garbage collector has no way of reclaiming it, and its dependencies can't be reclaimed unless the timer is terminated.

  • observer of an object
var btn = document.getElementById('btn');
function onClick (element) {
    element.innerHTMl = "I'm innerHTML"
}
btn.addEventListener('click', onClick);
  • 1
  • 2
  • 3
  • 4
  • 5

For the observer example above, it's important to explicitly remove them once they are no longer needed (or the associated object becomes unreachable). Older versions of IE 6 can't handle circular references. This is because older versions of IE are unable to detect the relationship between a DOM node and aJavaScript Circular references between code can lead to memory leaks.

However, modern browsers (including IE and MicrosoftEdge) With more advanced garbage collection algorithms (tagging), circular references can now be detected and handled correctly. I.e. you don't have to call removeEventListener to reclaim node memory.

3. Off the DOM references
The reason for this memory leak is simply this.
If you store the DOM as a dictionary (JSON key-value pairs) or an array, there are two references to the same DOM element: one in the DOM tree and the other in the dictionary. In the future, both references will need to be cleared.

// Referencing the DOM in objects
var elements = {
    btn: document.getElementById('btn')
}

function doSomeThing () {
    elements.btn.click();
}

function removeBtn () {
    // Remove the btn from the body, i.e. remove the btn from the DOM tree.
    document.body.removeChild(document.getElementById('button'));
    // But the global variable elements still retains a reference to btn, and btn still exists in memory and can't be reclaimed by the GC.
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

In this case, the reference can be cleared manually: = null

4. Closures
closure (math)The key effect is that anonymous functions can access variables in the parent scope.

function fn () {
    var a = "I'm a";
    return function () {
        console.log(a);
    };
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
'
(of a computer) run

Because variable a is referenced by an anonymous function within the fn() function, it is not recycled.

var globalVar = null; // Global variables
var fn = function () {
    var originVal = globalVar; // Local variables
    var unused = function () { // Unused functions
        if (originVal) {
            console.log('call')
        }
    }
    globalVar = {
        longStr: new Array(1000000).join('*'),
        someThing: function () {
            console.log('someThing')
        }
    }
}
setInterval(fn, 100);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

Each time the fn function is called, a new object originVal is created.
The variable unused is a closure that references originVal.
unused is not used, but the originVal it references forces it to stay in memory and not be reclaimed.
The solution is to set the originVal to null at the bottom of the fn.

Chrome Browser to view memory usage, follow the steps below.

  • Right-click on the web page, click "Check" to open the console (Mac shortcut option+command+i).
  • Select the Performance panel.
  • Check Memory, then click the little black Record button in the upper left corner to start recording.
  • Click Stop in the popup window to end the recording, and the panel will show the memory usage of this time.

summarize

Common memory leaks include:

  • Unexpected global variables
  • Forgotten timers or callback functions
  • Dereferencing the DOM
  • Repeatedly created variables in closures

How to avoid memory leaks

  • Pay attention to the logic of the program and avoid "dead loops" and the like;
  • Reduce unnecessary global variables, or objects with long life cycles, and garbage collect useless data in a timely manner
  • Avoid creating too many objects