The following are just some notes and personal understandings from books and blogs.

First, garbage recycling mechanism

1. JavaScript has a global execution environment and a function execution environment. When a function is executed, the function execution environment is pushed onto the stack and destroyed after execution. The global execution environment is destroyed when the page or browser is closed.

2. The execution environment is responsible for managing the memory used during code execution. For primitive types, value stores are stored in stack memory, while for reference types, references are stored in stack memory and values are stored in heap memory. Garbage collection is for values of reference types in heap memory.

Garbage collection mechanism: find those variables that are no longer in use, and then release their memory.

“Tracking which variables are used and which are not”

(1) reference counting method

Tracing idea: a variable is initialized to a value of a reference type with a reference count of 1. The reference count is incremented by one for each more reference and decayed by one for each less reference. When the reference count goes to zero, the memory occupied by the value of the reference type is freed. If {a:1} ends with a reference count of 0, {b:2} and {c:3} end with a reference count of 1, then the references to obj and obj1 are destroyed, so the reference count of their values becomes 0 and can be reclaimed.

function f(){ var obj={a:1}; //count({a:1})=1 var obj1=obj; / / count ({a: 1}) = 2 obj = {2} b: / / count ({a: 1}) = 1 obj1 = {3} c: / / count ({a: 1}) = 0, 1} {a: memory can be recalled}Copy the code

Problem: the reference count of a variable referenced in a loop will not be zero, and the value of the reference type cannot be released. For example, after the function is run, the references to obj and obj1 will be destroyed, but in the heap memory, the values of the two reference types will be referenced to each other by 1 and cannot be destroyed.

function f(){
    var obj={a:1};    //count({a:1})=1
    var obj1={b:2};   //count({b:2})=1
    obj.c=obj1;       //count({a:1})=2
    obj1.c=obj        //count({b:2})=2
}
Copy the code

In older versions of Internet Explorer before Internet Explorer 9, the garbage collection mechanism based on reference counting, circular references caused memory leaks.

(2) Marked removal method

Working principle:

The garbage collector marks all variables stored in memory at runtime (stack memory, heap memory).

2. Then, untag variables in the environment and those referenced by variables in the environment.

3. Then, the remaining marked variables will be considered as variables for deletion.

Root: “variables in the environment”, that is, variables in the variable object of the global execution environment in the execution stack, and variables in the variable object of the scope chain of the function execution environment. (Local variables and arguments of local functions, variables and arguments of other functions in the currently nested call chain)

Reachability: Variables that can be accessed through variables in the environment.

The idea of tracing is to get the “roots” and mark them. If the root is a reference type, mark the heap object to which the reference refers. If the object also has attributes of the reference type, mark the objects to which it refers, and so on, until an object has no attributes of the reference type. As you can see from the marking process, what is marked is reachable (accessible), and everything else is unreachable, that is, deletable. All but the reachable nodes that are marked are removed.

Reference links: segmentfault.com/a/119000001…

Take a look at how token clearance can be used to circumvent the problem of circular references.

function f(){
    var obj={a:1};    //count({a:1})=1
    var obj1={b:2};   //count({b:2})=1
    obj.c=obj1;       //count({a:1})=2
    obj1.c=obj        //count({b:2})=2
}
Copy the code

When this function runs, the reachable chain is marked as follows. Obj and obj1 are no longer root nodes, so the objects they point to are unreachable and eventually recycled.

Second, memory leakage

Reference links:

Juejin. Cn/post / 684490…

(a) What causes the memory leak?

The main cause of memory leaks is “unwanted (needed) references”.

A reference that is no longer needed refers to a reference to a piece of memory to which data is stored, and when the data is no longer needed by the developer (which is when the memory should be reclaimed), for some reason the occupied data is marked as active and unrecoverable and still exists in the root list tree. In the context of JavaScript, a reference that is no longer needed is first a variable that exists somewhere in the code and refers to a reference to a chunk of memory that should be reclaimed when it is no longer needed in the future. The memory leak was caused by developer error.

Source: juejin. Cn/post / 684490…

(2) The scene causing the leakage

Let’s start with the memory leak caused by reference counting in older Versions of Internet Explorer

Circular references: Older browsers use reference counting as a garbage collection mechanism and cannot recycle references to variables that cause memory leaks.

2. Event handler (event listener) : In the following example, both the myBtn element and the event handler are left in memory causing a memory leak, thinking that the element has been overwritten in the DOM number and is no longer needed, but its reference count is not zero.

<div id="myDiv"> <input type="button" value="Click me" id="myBtn"/> </div> <script> var btn=document.getElementById("myBtn"); //myBtn =function(){console.log("clicked") document.getElementById("myDiv").innerHTML="Processing..." ; </script> </script>Copy the code

The following describes memory leaks caused by the mark-clean garbage collection mechanism.

According to the “mark-clean” garbage collection mechanism, the following scenario should be analyzed where the memory leak object is reachable but no longer used in the code.

3. Global variables: a variable that is declared without using var, or a global variable that is declared deliberately and does not end up with null.

function foo(){
  msg= "this is a hidden global variable";
  console.log(msg);
}
Copy the code

In this scenario, the root of the global scope must be reachable. The function outputs this value at run time, but it is no longer needed after run, so it should be recycled. But because the developer inadvertently defined it as a global variable, it could not be reclaimed, resulting in a memory leak.

A reference in a timer callback or an event listener callback

(1) A reference inside the timer callback function is leaked

In the example below, the loop timer callback function runs into the environment stack where the local variable (node) is reachable. It also refers to the someResource variable in the outer scope, so foo() cannot destroy its variable object (the live object) after it has run, and is accessible through the loop timer callback’s execution environment’s scope chain, so someResource is reachable.

When a node node is removed from the page, the callback function does not enter the if statement, does not use the someResource, and expects to be recycled. But because the someResource variable is in the timer callback chain, it cannot be recycled. The data that someResource points to is a memory leak — essentially a memory leak caused by a closure. Best practice: When interval or timeout is not required, it is best to call clearInterval or clearTimeout to clear timers.

function foo(){ var someResource = getData(); setInterval(function(){ var node = document.getElementById('Node'); If (node){// Perform some operations on node and someResource node.innerhtml = json.stringify (someResource); }}, 1000); } foo () / / source link: https://juejin.cn/post/6844903553207631879Copy the code

(2) A reference to the event listener callback function is leaked. Once an event listener is no longer needed, it must be explicitly removed (or done before the associated object is to be reclaimed)

5, closures

When the closure function is created, it initializes the scope chain and already contains the variable object of the external function, so it takes up more memory than other functions. So improper closures can easily cause memory leaks. The leaking object is the variable object of the external function.

function fn2(){ let test = new Array(1000).fill('isboyjc') return function(){ console.log(test) } } let fn2Child = fn2() Fn2Child (source: https://juejin.cn/post/6984188410659340324, and closure deleted retun statementsCopy the code

Closure function even after running, closure of the scope chain is still cited fn2 variable object (knowledge points: function creates has the scope chain, and runtime function performs the scope chain is to create the environment of the scope chain front-end add function on its own variable object), so the fn2 variable object () will not be recycled. The example would look like this:

var flag=false; Function fn2(){let test = new Array(1000).fill('isboyjc') return function(){if(flag){ console.log(test) } } } let fn2Child = fn2() fn2Child()Copy the code

As with the leaky reference inside the timer callback, the closure function does not use the variable object of fn2(), but keeps referring to it, causing a memory leak.

Therefore, when the closure function is not in use, it should be set to null to unreference the anonymous function and free memory.

Invalid DOM references

In addition to the reference of the DOM tree to the node, I also created variables to point to the DOM node for convenient manipulation. When THE DOM node is removed using the DOM API, the actual node object is not recycled, because it is also referenced by other variables, so it cannot be recycled, resulting in memory leakage.

The following is an example of a direct copy:

<div id="root"> <ul id="ul"> <li></li> <li></li> <li id="li3"></li> <li></li> </ul> </div> <script> let root = document.querySelector('#root') let ul = document.querySelector('#ul') let li3 = document.querySelector('#li3') // Root. RemoveChild (ul) {ul = null; ul = null; At this time can GC li3 = null < / script > link: https://juejin.cn/post/6984188410659340324Copy the code

Garbage collection mechanism of V8 engine

JavaScript Advanced Programming mentions that the V8 engine tries to reclaim memory from closures, so it’s worth knowing about the V8 engine.

Because closures carry the scope of the function that contains them, they take up more memory than other functions. Overuse of closures can lead to excessive memory usage, and readers are advised to consider using closures only when absolutely necessary. While optimized JavaScript engines like V8 try to reclaim memory used by closures, use closures with caution.

The sources cited below are from the same article: juejin.cn/post/698158…

V8 engine garbage collection is based on generational garbage collection.

Cenozoic generation is the object of short survival time, old generation is the object of long survival time. New generation and old generation use different

Garbage collector (policy) for processing.

New generation: the Cheney (replication) algorithm

1. Divide the new generation of heap memory into two parts: one is the space in use, which is called the use area, and the other is the space in idle state, which is called the free area. 2. Start garbage collection when the use area is almost full. Mark the active objects in the use area. Finally, swap roles, turn the original use area into idle area, and turn the original idle area into use area.

Garbage collection is also the work of JS main thread (JS engine thread). In order to avoid garbage collection, JS main thread completely stops for too long, garbage collector in the main thread execution process, open multiple auxiliary threads, perform the same cleaning work at the same time, speed up the completion of the collection work.

Old generation: mark-sweep strategy.

1. Incremental marking is adopted instead of full pause marking.

Increments are the process of dividing a GC tag into small steps, with the application logic running for a while after each small step, alternating several times to complete a GC tag round.

2. Mark completion with lazy cleanup. After the delta mark is complete, the cleanup is deferred if there is sufficient memory. There is no need to clean up at one time.

Even so, with incremental tags and lazy cleanup, the main JS thread pauses for less time each time, but the total pause time is not less.

3. Concurrent collection is to let garbage collection be executed by another thread, so that garbage collection does not affect the JS main thread operation.

Old-generation fusion uses strategies such as incremental markup, lazy cleanup, and concurrent collection. While the main JS thread is executing, the worker thread can mark the main thread as not blocking (concurrent flag); When the main thread of JS is cleaned, the auxiliary thread is used for cleaning, and the cleaning is done in increments in batches (parallel collection).

Other reference articles: www.jianshu.com/p/b8ed21e8a…