JS operations that cause memory leaks

The answer:

1) Memory leaks caused by unexpected global variables

function leak() { leak = "xxx"; //leak becomes a global variable and is not recycled}Copy the code

2) Memory leaks caused by closures

function bindEvent() {
  var obj = document.createElement("XXX");
  obj.οnclick = function() {
    //Even if it's a empty function
  };
}
Copy the code

Closures can hold local variables in a function without freeing them. The above example defines the event callback, because the function is defined within the function, and the reference to the inner function, the event callback, is exposed, forming a closure. 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.

Function onclickHandler() {//do something} function bindEvent() {var obj = document.createElement("XXX"); Obj. &western nclick = onclickHandler; Function bindEvent() {var obj = document.createElement("XXX"); var obj = document.createElement("XXX"); Obj.ο nclick = function() {//Even if it's an empty function}; obj = null; }Copy the code

3) No clean REFERENCES to DOM elements

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'))
}
Copy the code

4) Forgotten timers or callbacks

var someResouce = getData(); setInterval(function() { var node = document.getElementById("Node"); if (node) { node.innerHTML = JSON.stringify(someResouce); }}, 1000);Copy the code

This code is very common. If the Node element is removed from the DOM, the timer will still exist, and since the callback contains a reference to the someResource, the someResource outside the timer will not be released.

5) Memory leaks caused by the presence of child elements

In memory, red refers to the child element refB that is indirectly referenced by js variables. As shown in the figure above, refB is indirectly referenced by refA, which will not be recycled even if the refB variable is empty. As long as it is not deleted, all of its parent elements (shown in red) are not deleted.

6) Problems with ie 7/8 reference counting using circular references

function fn() {
  var a = {};
  var b = {};
  a.pro = b;
  b.pro = a;
}
fn();
Copy the code

After fn() is executed, both objects have left the environment, which is fine under the token clearing method. However, under the reference counting strategy, since the number of references of A and B is not zero, memory will not be reclaimed by the garbage collector. If the FN function is called a lot, memory leaks will occur. On IE7 and IE8, the memory went straight up. Some objects in IE are not native JS objects. For example, the memory-leaking DOM and BOM objects are implemented as COM objects using C++, and the garbage collection mechanism for COM objects uses a reference counting strategy. Therefore, the COM objects accessed by JS are still based on the reference counting strategy, even though the JS engine of IE adopts the tag clearing strategy. In other words, whenever COM objects are involved in IE, there is a problem with circular references.

var element = document.getElementById("some_element");
var myObject = new Object();
myObject.e = element;
element.o = myObject;
Copy the code

The above example creates a circular reference between a DOM element (Element) and a native JS object (myObject). The variable myObject has a property named E that points to the Element object; The element variable also has an attribute called o that refers to myObject. Because of this circular reference, even if the DOM in the example is removed from the page, it will never be recycled.

Look at the example above, some people think too weak, who would do such a boring thing, but actually we do it all the time

Window. &western nl &western AD = function outerFunction () {var obj = document. GetElementById (" element ") : Obj. &western nclick = function innerFunction () {}; };Copy the code

This code looks fine, but obj references document.getelementById (” Element “), and the onclick method of Document.getelementById (” Element “) references variables in the external environment, Including OBJ, of course. Isn’t that hidden?

The easiest way to do this is to manually unreference the loop yourself, as we did with the function above

myObject.element=null; element.o=null; Window. &western nl &western AD = function outerFunction () {var obj = document. GetElementById (" element ") : Obj. &western nclick = function innerFunction () {}; obj=null; };Copy the code

Setting a variable to NULL means severing the connection between the variable and the value it previously referenced. The next time the garbage collector runs, it removes these values and reclaims the memory they occupy. Note that IE9+ does not have a Dom memory leak caused by circular references, either because Microsoft has optimized it or because Dom recycling has changed

Resolution:

1. Recycle mechanism of JS

The mechanism of JavaScript garbage collection is simple: find variables that are no longer used and free their memory, but this process is not real-time because it is expensive, so the garbage collection system (GC) performs it periodically at regular intervals.

Which variable is useless? So the garbage collector must keep track of which variables are useless, and mark those that are no longer useful, so they can be reclaimed in the future. The strategy used to tag useless variables may vary by implementation, and there are usually two implementations: tag clearing and reference counting. Reference counting is less common, and tag clearing is more common.

2. Mark and sweep

The most common garbage collection method in JS is tag cleanup. When a variable enters the environment, for example, declaring a variable in a function, mark the variable as “entering the environment.” Logically, you can never free up memory occupied by variables that enter the environment, because they may be used whenever the execution stream enters the corresponding environment. When a variable leaves the environment, it is marked as “out of the environment.”

function test() { var a = 10; Var b = 20; } test(); // After the execution, a and B are marked to leave the environment and recycledCopy the code

3. Reference counting

The meaning of reference counting is to keep track of how many times each value is referenced. When a variable is declared and a function object array is assigned to the variable, the number of references to the value is 1. If the same value is assigned to another variable, the number of references to the value is increased by one. Conversely, if a variable containing a reference to that value obtains another value, the number of references to that value is reduced by one. When the number of references to this value goes to zero, there is no way to access the value, and the memory space it occupies can be reclaimed. This way, the next time the garbage collector runs, it frees up memory for values that are referenced zero times.

function test() { var a = {}; Var b = a; Var c = a; var c = a; Var b = {}; var b = {}; // the number of references to a is reduced by 1, to 1}Copy the code

4. How to analyze memory usage

Google Chrome provides a very powerful JS debugging tool. The Memory Profiles view allows you to take snapshots of the Memory in which your JavaScript code is running and compare those snapshots. It also lets you keep track of memory allocations over time. Different types of lists can be displayed in each result view, but the most useful ones for us are the summary and comparison lists. The summary view provides different types of allocated objects and their total size: shallow size (the sum of all objects of a particular type) and retained size (shallow size plus the size of the other objects that retained the object). Distance shows the shortest distance an object can travel to the GC root. The Comparison view provides the same information but allows you to compare different snapshots. This is very helpful in finding leaks.

5. How to avoid memory leaks

1) Reduce unnecessary global variables or objects with long life cycle, and timely garbage collection of useless data;

2) Pay attention to program logic, avoid “endless loop” and so on;

3) Avoid creating too many objects rule: Return what you don’t use.