This is the fifth day of my participation in the August More text Challenge. For details, see: August More Text Challenge

-> Go to my original blog

Antecedents feed

1. Memory management 2. Garbage Collection and GC algorithm 3Copy the code

Memory management

Memory: composed of read-write units, representing a piece of operational space

Management: to manipulate the application, use and release of a space artificially

Memory management: Developers actively apply for space, use space, and release space

Management process: apply – use – release

Js memory management

/ / application
let obj={}
/ / use
obj.name='ssss'
/ / release
obj=null
Copy the code

JS

Js memory management is automatic

Objects are garbage when they are no longer referenced

Objects that cannot be accessed from the root are garbage

Js reachable objects

Objects that are accessible are reachable objects (references, chains of scope)

The criterion of reachability is whether it can be found from the root

The root in js can be understood as a global variable object

GC algorithm

GC can find garbage in memory and free and reclaim space.

What is garbage in GC

Such as:

1. Objects that are no longer needed in the program
function func(){
    name='lh'
    return `hi!${name}`
}
func()
Copy the code
2. Objects in the program that are no longer accessed
function func(){
    const name='lh'
    return `hi!${name}`
}
func()
Copy the code

GC is a mechanism. The garbage collector completes the specific work, which is to find the garbage free space and recycle the space. The algorithm is the rules to find and recycle the garbage when working.

Common GC algorithm

  1. Reference count: Determines whether the current object is garbage by a number
  2. Tag cleanup: Adds tags to live objects at work to determine if they are garbage
  3. Tag sorting: Similar to tag clearing, the subsequent recycling process is different from tag clearing
  4. Generational collection: The collection mechanism in V8

Reference counting algorithm implementation principle

Set the number of references to check whether the current number of references is 0.

Reference counter

When the reference relationship changes, the reference value is modified. If a new reference +1 appears in the code, subtract one reference -1.

Recycle immediately when the reference number is 0.

const user1={age:1}
const user2={age:2}
const user3={age:3}

const nameList=[user1.age,user2.age,user3.age] // User1, user2, and user3 are not 0 and will not be reclaimed immediately

function fn(){
    const num1=1
    const num2=2
} // Once the execution reaches this position, num1 and num2 counts are 0 and are reclaimed immediately

fn()
Copy the code

Advantages and disadvantages of reference counting algorithms

Advantages: Garbage is collected immediately when found, minimizing program pauses

Disadvantages: Unable to recycle circular reference objects, high time cost (reference count needs to maintain a numeric change, in the current situation should always monitor whether the reference value of the current object needs to be modified, if the current environment has many objects to be modified, then the time cost will appear greater)

function fn(){
    const obj1={}
    const obj2={}

    obj1.name=obj2
    obj2.name=obj1

    return 'jjj'
}

fn()
Copy the code

As above, obj1-><-obj2 both reference counts are 1, so it will not be reclaimed even after the function is run.

The realization principle of the tag clearing algorithm

There are two stages: marking and clearing.

Tag phase: The active object is found and marked through all the objects

Clear phase: clear unmarked objects and remove the first marked objects.

The space is reclaimed by two iterations

Advantages and disadvantages of the tag clearing algorithm

Advantages: Resolves recycling of circular references over reference counting.

Disadvantages: In the marker clearing algorithm, the reclaimed space is put into the free list, which may make the current address discontinuous, and the reclaimed space is scattered to all corners, resulting in space fragmentation. If you want to apply for a space that does not match these Spaces, then it is not suitable and can not maximize the use of space. Garbage objects are not immediately collected.

The principle of tag sorting algorithm

Tag collation can be seen as an enhancement of tag cleanup, and the tag phase is consistent with tag cleanup.

In the cleanup phase, the cleanup is performed first, moving the object’s position so that the space after its reclamation is continuous.

Advantages: Reduced space fragmentation.

Disadvantages: Garbage objects are not immediately collected.

V8

Know the V8

V8 is a mainstream JS execution engine, using just-in-time compilation, V8 memory limits (not more than 1.5g for 64-bit, not more than 800M for 32-bit).

V8 garbage collection policy

The memory is divided into the new generation and the old generation, and different algorithms are adopted for different generations. V8’s memory space is partly for new-generation object storage (using a specific GC algorithm) and partly for old-generation storage (using a specific algorithm).

The GC algorithm commonly used in V8

  1. Generational recycling
  2. Space to copy
  3. Mark clear
  4. Tag to sort out
  5. Mark the incremental

How does V8 recycle next-generation objects

V8 memory is divided into 2, little space to store the new generation object (32 m | 16 m), new generation refers to the shorter survival time of the object.

The new generation object recycling process adopts the copy algorithm + mark sorting. The new generation of memory is divided into two equally sized Spaces, From and to. From is used space, and to is free space. Live objects are stored in the FROM space and copied to after tag collation. The space is freed by swapping from and to.

Recycling details

In the process of copying, there may be promotion, i.e. moving the new generation object to the old generation. If a new generation survives one GC, it needs to be promoted and copied into the old generation. If the To space is more than 25% used, it will also be copied To the old generation.

How does V8 recycle old generation objects

The old generation object is stored in the right old generation area, 64 bit 1.4g, 32 bit 700M. Old age objects are objects (global variables, closures) that have a long life.

In the old era, it mainly adopts the algorithm of mark clearing, mark sorting and incremental mark. First, the garbage space is collected using tag cleaning. If you want to move the content of the new generation to the old generation and there is not enough space for the old generation, the space will be optimized using tag cleaning. Incremental marking is used for efficiency optimization.

The Cenozoic regional GC uses space for time (replication); the older GC is not suitable for replication algorithms

How do markup increments optimize garbage collection

Garbage collection works while blocking program execution. After the program is executed, once the garbage collection starts, it iterates over the object to mark (increments), segments the current entire GC operation, and lets the garbage collection execute alternately with the program until the garbage collection is complete.

Monitor the memory

Standards for defining memory issues

Memory leak: Memory continues to rise

Memory ballooning: Not supported by the device hardware

Frequent garbage collection: Through memory change map

Several ways to monitor memory

Browser Task Manager

Timeline Records the timing diagram

Heap snapshot lookup for detached DOM

Determine if there is frequent garbage collection

The heap snapshot separates the DOM

What is detached DOM

Dom nodes that are detached from the DOM tree, but are referenced by JS, are called detached DOM.

You can’t see it on the interface, but you can see it on the memory, find it through the heap snapshot, and process to free the memory.

In the following example, clicking a button creates the detached DOM

var tmpEle
function fn(){
    const ul=document.createElement('ul')
        for(let i=0; i<10; i++){const li=document.createElement('li')
            ul.appendChild(li)
         }
         tmpEle=ul
}
document.getElementById('btn').addEventListener('click',fn)
Copy the code

Detached HTMLUListElement and detached HTMLLIElementĂ—10 are displayed in the detached console. In chrome-> F12 -> Memory, take a snapshot before and after clicking the Add button.

So let’s comment out that reference line, and take a picture again without producing a detached DOM

    function fn(){
                const ul=document.createElement('ul')
                for(let i=0; i<10; i++){const li=document.createElement('li')
                    ul.appendChild(li)
                }
                //tmpEle=ul
            }
Copy the code

Determine whether GC is frequent

When GC is working, the application is stopped. When GC is working too frequently and too long, the application is suspended animation, and the user feels stuck.

1. Judge by the trend in the Timeline (blue bars are displayed in the timeline)

2. Use the task manager to increase or decrease data frequently

How to accurately test JS performance

A large number of execution samples were used for mathematical statistics and analysis. Use jsbench. Me based on benchmark.js.

Some tips for optimizing performance

Use global variables with caution

  1. Global variables always exist in the global execution context, at the top of all scope chains. If the variable is not found locally, it is searched layer by layer along the scope chain until it reaches the top of the scope, which is very time consuming.
  2. The global execution context always exists in the context execution stack until the exit. Because the global variables are always active in the context execution stack, the memory usage of the program will be reduced.
  3. A local scope that defines variables of the same name obscures or contaminates the global scope.

Cache global variables

Cache global variables that cannot be avoided in use locally as follows:

<button id="Add1">Add1</button>
<button id="Add2">Add2</button>
<button id="Add3">Add3</button>
<button id="Add4">Add4</button>
<button id="Add5">Add5</button>
<button id="Add6">Add6</button>
<button id="Add7">Add7</button>
Copy the code

Case1:

function getBtn1(){
                const btn1=document.getElementById('Add1')
                const btn4=document.getElementById('Add4')
                const btn2=document.getElementById('Add2')
                const btn3=document.getElementById('Add3')
                const btn5=document.getElementById('Add5')}Copy the code

Case 2 Cache document;

function Btn2(){
                const doc=document
                const btn1=doc.getElementById('Add1')
                const btn4=doc.getElementById('Add4')
                const btn2=doc.getElementById('Add2')
                const btn3=doc.getElementById('Add3')
                const btn5=doc.getElementById('Add5')}Copy the code

Turning on jsbench for performance testing, Case2 was 2% faster than CasE1.

Add additional methods through the prototype object

Adding methods to prototype objects is better than adding methods to constructors.

//case1
const func1=function(){}
func1.prototype.foo=function(){console.log(111)}
const obj1=new func1()
// case2
const func2=function(){
    this.foo=function(){console.log(111)}}const obj2=new func2()
Copy the code

Also put on jsbench test, case1 is 18% faster than case!!

Avoid the closure trap

Closures are powerful, but can leak memory if used improperly. Don’t use closures for their own sake.

A very common example:

function bindEvent(){
    const el=document.getElementById('btn')
    el.onclick=function(){
        console.log(el.id)
    }
}

bindEvent()
Copy the code

After bindEvent is executed, there is still a reference to EL in el.onclick, causing a memory leak. This can be modified as follows:

function bindEvent(){
    const el=document.getElementById('btn')
    const str=el.id
    el.onclick=function(){
        console.log(str)
    }
    el=null // The event is already bound to the DOM. Clearing this variable clears all references
}

bindEvent()
Copy the code

Avoid using property access methods

  1. Js does not need access methods for properties because all properties are externally visible
  2. Using attribute access methods only adds another layer of redefinition, with no control over access

For loop optimization

Such as:

const arr=[.....]
for(let i=0; i<arr.length; i++){.... }/ / as
for(let i=0,len=arr.length; i<len; i++){.... }Copy the code

Use the optimal cycle

High to low efficiency: foreach>for>for.. in..

Node addition optimization

The addition of nodes inevitably leads to backflow and redrawing. When inserting a document, we should try to use document fragmentation as follows:

// case1
for(let i=0; i<10; i++){const el=document.createElement('p')
    el.innerHTML=i
    document.body.appendChild(el)
}
// case2
const wrapper=document.createDocumentFragment()
for(let i=0; i<10; i++){const el=document.createElement('p')
    el.innerHTML=i
    wrapper.appendChild(el)
}
document.body.appendChild(wrapper)
Copy the code

Entering jSbench test performance, CasE2 was 10% faster than CasE1

Replace new Object with a direct quantity

const arr=new Array(1.2.3)

const arr=[1.2.3] // better!!
Copy the code