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
- Reference count: Determines whether the current object is garbage by a number
- Tag cleanup: Adds tags to live objects at work to determine if they are garbage
- Tag sorting: Similar to tag clearing, the subsequent recycling process is different from tag clearing
- 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
- Generational recycling
- Space to copy
- Mark clear
- Tag to sort out
- 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
- 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.
- 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.
- 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
- Js does not need access methods for properties because all properties are externally visible
- 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