preface
In the JVM column I created earlier, I explained the garbage collection mechanism in the Java Virtual machine. Just like the JVM has a garbage collection mechanism, JavaScript also has a garbage collection mechanism.
It is well known that the application is running in the process of take up some memory space, and after the operation will have to release the memory no longer used, can appear otherwise continue to rise, memory in the below image will affect the speed of the program running on one hand, on the other hand, serious word will lead to the collapse of the entire program.
Memory management in JavaScript
- Memory: composed of read and write units, representing a piece of operable space;
- Management: to operate the application, use and release of a space artificially;
- Memory management: developers actively apply for space, use space, release space;
- Management process: application – Use – Release
Some languages (such as C) need to manually release the memory, but it can be very troublesome. Therefore, many languages, such as JAVA, provide an automatic memory management mechanism called “Garbage Collecation”, and JavaScript also provides Garbage Collecation (GC).
Stop The World
Before introducing the garbage collection algorithm, let’s look at “total pauses.” Before The garbage collection algorithm is implemented, The application logic needs to be paused and then executed after The garbage collection. This behavior is called “Stop The World”. For example, if a GC takes 50ms, the application logic pauses for 50ms.
The purpose of the total pause is to resolve inconsistencies between the application logic and what the garbage collector sees.
Take, for example, eating at a cafeteria and coming back happily from your food only to find that the waiter has taken away your cutlery. Here, the server is the garbage collector, the cutlery is the distribution object, and we are the application logic. From our point of view, the cutlery is temporarily placed on the table, but the waiter seems to think you don’t need it anymore, so they take it away. You and the waiter do not see the same thing, which leads to the waiter doing something we do not expect. Therefore, to prevent the application logic from being inconsistent with what the garbage collector sees, the garbage collection algorithm needs to stop the application logic during execution.
JavaScript
Garbage collection in
Examples of situations that would be considered garbage in JavaScript are as follows:
- The object is no longer referenced;
- The object can not be asked from the root petition;
GC
algorithm
Common GC algorithms are as follows:
- Reference counting
- Mark clear
- Tag to sort out
- Generational recycling
Reference counting
The most common garbage collection method used by early browsers is called “reference counting” : the language engine has a “reference table” that holds the number of references to all resources (usually values) in memory. If the number of references to a value is zero, the value is no longer needed, so it can be freed.
const user1 = {age: 11}
const user2 = {age: 22}
const user3 = {age: 33}
const userList = [user1.age, user2.age, user3.age]
Copy the code
User1, user2, and user3 are referenced by the userList, so their reference count is not zero and will not be recycled
function fn() {
const num1 = 1
const num2 = 2
}
fn()
Copy the code
After the fn function is executed, num1 and num2 are local variables. After the fn function is executed, their reference count will be zero and all such code will be recycled as “garbage”.
Reference counting algorithms have one big problem: circular references
function objGroup(obj1, obj2) {
obj1.next = obj2
obj2.prev = obj1
return {
o1: obj1,
o2: obj2,
}
}
let obj = objGroup({name: 'obj1'}, {name: 'obj2'})
console.log(obj)
Copy the code
In the example above, obj1 and obj2 refer to each other through their respective attributes, and none of them have a zero reference count so that they are not collected by the garbage collection mechanism, resulting in a waste of memory.
In fact, reference counting algorithm has a relatively big disadvantage, is that we need to set aside a separate space to maintain the reference count of each variable, which for large programs, space overhead is relatively large.
Advantages of reference counting algorithm:
- When the reference count is zero, garbage is immediately collected.
- Minimizing program pauses;
Disadvantages of reference counting algorithm:
- Unable to recycle objects referenced by loop;
- Large space overhead;
Mark-sweep
Core idea: mark and clear two stages to complete.
- Traverse all objects to find mark active objects;
- Iterate over all objects to clear unmarked objects;
- Reclaim space.
The advantages of the tag clearing algorithm are as follows: Compared with the reference counting algorithm, the biggest advantage of the tag clearing algorithm is that it can recycle the referenced objects, and it is also the algorithm most used by V8 engine.
The disadvantages of the tag clearing algorithm are:
In the figure above, we can see that the red area is a root object, which is a global variable, and will be marked; Blue areas are unmarked objects that are collected by the collection mechanism. There is a problem. On the surface, the blue area has reclaimed three Spaces, but these three Spaces are discontinuous. When we have an object that needs three Spaces, the space we just reclaimed cannot be allocated, which is called “space fragmentation”.
Mark-compact
In order to solve the problem of memory fragmentation and improve the utilization of memory, a tag collation algorithm is introduced.
Tag cleanup can be seen as an enhancement to tag cleanup. The operation of the tag phase is the same as that of tag clearing.
The cleanup phase performs a cleanup, moving object locations, moving surviving objects to one side, and then cleaning up memory outside end boundaries.
The disadvantage of tag collation is that objects are not immediately recycled when they are moved.
Incremental Marking
To reduce the total pause time,V8
The markup has been optimized to break the markup process of a pause into many small steps. After each small step, let the application logic run for a few moments, and do this multiple times before completing the mark.
Prolonged GC, which leads to application pauses and unresponsiveness, can lead to a poor user experience. Starting in 2011, v8 replaced the “total pause” flag with an incremental one. The improved marking method reduces the maximum pause time to 1/6 of the original.
V8 engine garbage collection strategy
- Using the idea of generation recycling;
- Memory is divided into new generation, old generation;
Different algorithms are used for different objects:
(1) Cenozoic: The survival time of objects is short. Newborn objects or objects that have been garbage collected only once.
(2) Old generation: the object lives for a long time. Objects that have undergone one or more garbage collections.
The V8 stack is equal to the new generation plus the old generation. And for different operating systems to do the space of the memory limit.
Type \ System bits | A 64 – bit | 32 – |
---|---|---|
The old generation | 1400MB | 700MB |
The new generation | 32MB | 700MB |
Reasons for limiting memory:
This is more than enough memory for a browser.
According to the GC mechanism of the browser, after continuous testing, if the memory is set a little larger, the GC recovery time will reach the user’s perception, which will cause the perception of the lag.
Reclaiming new generation objects
The recycling of new generation objects is based on the Scavenge algorithm and marking algorithm. The Cheney algorithm is used to implement Scavenge.
The Cheney algorithm divides memory into two equally large Spaces, the used space is From and the free space is To.
If the object is alive, check whether it meets the promotion conditions. If it meets the conditions, it will be promoted To the old generation. Otherwise, the object will be copied From the From space To the To space. If the object is not viable, the space of the nonviable object is freed. After the replication is complete, the roles of the From space and To space are flipped.
Object promotion mechanism
A new generation of GC still alive needs to be promoted. When an object is copied From the From space To the To space, if more than 25% of the To space is used, the object is promoted directly To the old generation. The reason for the 25% ratio is that when recycled Scavenge is completed, the To space is flipped To the From space To continue the allocation of object memory. If the proportion is too large, subsequent memory allocation will be affected.
Reclaim old generation objects
The old generation objects are reclaimed mainly by token clearing, token collation and incremental marking algorithms, mainly by token clearing algorithm, and only in the case of insufficient memory allocation, token collation algorithm is adopted.
- First of all, the garbage space is collected with the tag clearance.
- Spatial optimization was carried out by label finishing.
- Incremental marking is used to optimize efficiency.
Comparison of Cenozoic and old generation recycling
Because the Cenozoic generation occupies less space, space – time mechanism is adopted. Old generation area space is relatively large, not suitable for a large number of copy algorithm and mark sorting, so the most commonly used is the mark clearing algorithm, in order to minimize the total pause time.
Memory leak identification method
Let’s start with some memory-consuming code:
<button class=" BTN "> Click </button> <script> const BTN = document.querySelector('.btn') const arrList = [] btn.onclick = function() { for(let i = 0; i < 100000; I++) {const p = document. The createElement method (' p ') / / p.i nnerHTML = 'I am a p element' document. The body. The appendChild (p)} arrList. Push (new Array(1000000).join('x')) } </script>Copy the code
browser-basedPerformance
To monitor memory changes
Click Record, and then we do the actions that we feel consume performance, and when we’re done, clickstop
Stop recording.
And then we look at what’s causing the memory leak, and we just focus on memory.
You can see that there is a little groove in the memory where the short time consumption is faster, and the drop is that the browser is doing garbage collection.
Performance optimization
- Avoid using global variables
- Global variables are mounted under the window;
- Global variables have at least one reference count;
- Global variables live longer and continue to consume memory;
- In the case of clear data scope, try to use local variables;
- Reduce the level of judgment
Function doSomething(part, chapter) {const parts = ['ES2016', 'Vue', 'React', 'Node'] if (part) {if (parts.includes(part)) {console.log(' chapter > 5 ') {console.log(' you need to provide VIP status ')}} } else {console.log(' please confirm module info ')}} doSomething('Vue', 6) Chapter) {const parts = ['ES2016', 'Vue', 'React', 'Node'] if (! Part) {console.log(' Please confirm module information ') return} if (! Parts.includes (part)) return console.log(' includes ') if (chapter > 5) {console.log(' includes ')}} doSomething('Vue', 6)Copy the code
- Reduce data reads For frequently used data, we cache the data.
<div id="skip" class="skip"></div>
<script>
var oBox = document.getElementById('skip')
// function hasEle (ele, cls) {
// return ele.className === cls
// }
function hasEle (ele, cls) {
const className = ele.className
return className === cls
}
console.log(hasEle(oBox, 'skip'))
</script>
Copy the code
- Reduce activity in the circulatory body
Var test = () => {var I var arr = ['Hello World ', 25, 'Hello World '] for(I = 0; i < arr.length; I ++) {console.log(arr[I])}} Var test = () => {var I var arr = ['Hello World ', 25, 'Hello World '] var len = arr. Length for(I = 0; i < len; i++) { console.log(arr[i]) } }Copy the code
- Event binding optimization
<ul class="ul"> <li>Hello World! </li> <li>25</li> <li> Robe with child < / li > < / ul > < script > var list = document. QuerySelectorAll (' li ') function showTxt (ev) { Console. log(ev.target.innerhtml)} for (item of list) {item.onclick = showTxt} function showTxt(ev) {var target = ev.target if (target.nodeName.toLowerCase() === 'li') { console.log(ev.target.innerHTML) } } var ul = document.querySelector('.ul') ul.addEventListener('click', showTxt) </script>Copy the code
- Avoid closure traps
<button class=" BTN "> click </button> <script> function foo() {let el = document.querySelector('.btn') el.onclick = Function () {console.log(el.classname)}} foo() function foo1() {let el = document.querySelector('.btn') El.onclick = function() {console.log(el.classname)} el = null // Set el to null to prevent references in closures from being recycled} foo1() </script>Copy the code