- Memory management
- Garbage collection and common GC algorithms
- V8 engine garbage collection
- The Performance tools
- Code optimization examples
Memory management
- Memory: Composed of read and write units, representing a piece of operable space
- Management: the application, use, and release of a space considered to operate
- Memory management: developers actively apply for space, use space, release space
- Management process: apply – use – release
// Request space
let obj = {}
// Use space
obj.name = 'lg'
// Free up space
obj = null
Copy the code
Garbage collection in JavaScript
Garbage in JavaScript
- Memory management is automatic in JavaScript
- Objects are no longer referenced as garbage
- Objects cannot petition from the root to ask about constant garbage
Reachable objects in JavaScript
- Accessible objects are reachable objects (references, scope chains)
- The criterion of reachability is whether it can be found from the root
- The root in JavaScript can be understood as the —————— global variable object
References and reachability in JavaScript
// reference
let obj = { name: 'xm' } // reference + 1
let ali = obj // reference + 1
console.log(obj) / / can reach
obj = null // Obj is still reachable because ali is still referencing obj
Copy the code
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
From the root, an object becomes garbage if all of its access routes are destroyed
GC algorithm
GC definitions and functions
- GC is short for garbage collection mechanism
- The GC finds garbage in memory and frees and reclaims space
What is the garbage in the GC
- Objects that are no longer needed in a program
function func () {
name = 'lg'
return name
}
func()
Copy the code
- An object in a program that can no longer be accessed
function func () {
const name = 'lg'
return name
}
func()
Copy the code
What is the GC algorithm
- GC is a mechanism where the garbage collector does the specific work
- The content of the work is to find the garbage release space, recycling space
- An algorithm is a set of rules that lookup and recycle follow at work
Common GC algorithms
- Reference counting
- Core idea: Set the number of references to determine whether the current number of references is 0
- Reference counter
- Modify the reference number when the reference relationship changes
- If the reference number is 0, recycle it immediately
- advantages
- Recycle rubbish as soon as it is found
- Minimize program pauses
- disadvantages
- Object referenced by loop cannot be recycled
- High time consumption (monitoring object modification time)
function fn () { const obj1 = {} const obj2 = {} obj1.name = obj2 obj2.name = obj1 } fn () Copy the code
- Mark clear
- Core idea: mark and clear two stages to complete
- Traverse through all objects to find the mark active object
- Iterate over all objects to remove unmarked objects
- Reclaim space
- advantages
- Relative to reference technology, object circular reference can be solved as shown in figure A1, b1
- disadvantages
- Garbage objects are not immediately recycled in two steps (mark, clean)
- Garbage fragments recovered, memory addresses discontinuous ———— spatial fragmentation
- Tag to sort out
-
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 limits the cleanup, moving objects around
-
advantages
- Reduce the space for fragmentation
- Garbage objects are not collected immediately
-
- Generational recycling
V8
- V8 is a mainstream JavaScript execution engine
- V8 uses instant compilation (source code -> machine code)
- V8 Memory limit (64: <= 1.5G, 32: <= 800M)
V8 garbage collection strategy
- Adopt the idea of generational recycling
- Memory is divided into new generation, old generation
- Different algorithms are used for different objects
The common GC algorithm in V8
- Generational recycling
- Space to copy
- Mark clear
- Tag to sort out
- Mark the incremental
How does V8 recycle new generation objects
- V8 memory space is split in two
- Little space to store the new generation object (32 m | 16 m)
- Cenozoic refers to objects that have a short life span
New generation object recovery implementation
- The recycling process adopts replication algorithm + label finishing
- The new generation of memory is divided into two equal size Spaces
- The use space is
From
, the free space isTo
- Live objects are stored in
From
space - The live object is copied to
To
From
与To
Swap space is freed
Recovery Details
- Promotions may occur during copying
- Promotion is the movement of the new generation to the old generation
- round
GC
The new generation that is still alive needs to be promoted To
Space utilization above 25% requires promotion
How does V8 recycle old generation objects
- Old generation objects are stored in the old generation area on the right
- 64: <= 1.4GB, 32 OS <= 700M
- An old generation object is an object that lives for a long time
Old generation object recycling implementation
- It mainly adopts the algorithm of mark clearing, mark finishing and increment mark
- The garbage collection space is first collected using the tag sweep
- Space optimization with markup arrangement (lack of space when promotion occurs)
- Incremental marking is used for efficiency optimization
Details of the contrast
- New generation of regional garbage recycling use space for time
- Old-generation area garbage collection is not suitable for replication algorithms
How do tag deltas optimize garbage collection
Performance Tool Introduction
Why use Performance
- The purpose of GC is to achieve a virtuous cycle of memory space
- The cornerstone of a virtuous cycle is rational use
- Keep an eye on it to see if it makes sense
- Performance Provides multiple monitoring modes for real-time monitoring
Memory problems
- Pages load lazily or pause frequently
- The page continues to have poor performance
- Page performance deteriorates over time
Several ways to monitor memory
- Memory leak: Memory usage continues to rise
- Memory bloat: Performance issues on most devices
- Frequent garbage collection: Analyzed by memory variation graph
methods
- Browser task manager
- Timeline Records the Timeline diagram
- Split DOM for snapshot lookup
- Determine if there is frequent garbage collection
V8 engine workflow
- Scanner is a Scanner (lexical analysis)
- Parser is a Parser (generates an AST)
- The PreParser pre-resolver
- Skip unused code
- Instead of generating an AST, create scopes without variable references and declarations
- Throws specific errors based on the specification
- Faster parsing
- Full amount of parsing
- Parse the code being used
- Generate AST
- Build specific scopes information, variable references, declarations, etc
- Throws all syntax errors
- The PreParser pre-resolver
- Ignition is an interpreter provided by V8 (AST => bytecode)
- TurboFan is a compiler module supplied by V8 (bytecode => machine code)
The stack processing
The stack to prepare
- JS execution environment
- ECStack (Execution Context stack)
- Execution context
- VO(G) global variable object
Object stack execution
Function stack execution
- Function creates
- The function name can be regarded as a variable, stored in VO, and its value is the memory address corresponding to the current function
- The function itself is also an object. When it is created, it has a memory address that holds the function body code (as a string).
- Function performs
- Function execution generates a new private context with an AO to manage variables in this context
- Determine the scope chain < current execution context, execution context where the parent scope resides >
- To determine this
- To initialize the arguments
- Parameter assignment: this is equivalent to a variable declaration and then places the declared variable in the AO
- Variable ascension
- Code execution
Closure stack processing
- Closures are a mechanism
- Variables in the current context do not interfere with variables in other contexts
- Data in the current context (heap memory) is referenced by variables in contexts other than the current context, and the data is saved
- When a function is called, a new context is formed, and the current context is not released after the function is called
Closures and garbage collection
Loop to add the implementation of the event
- Foundation (with certain problems)
for (var i = 0; i < aButtons.length; i++) {
aButtons[i].onclick = function () {
console.log('The current index is${i}`) // Always print 2}}Copy the code
- Closures (performance is not good enough and requires a lot of memory)
for (var i = 0; i < aButtons.length; i++) {
(function (i) {
aButtons[i].onclick = function () {
console.log('The current index is${i}`)
}
})(i)
}
for (var i = 0; i < aButtons.length; i++) {
aButtons[i].onclick = (function (i) {
return function () {
console.log('The current index is${i}`)
}
})(i)
}
Copy the code
- Var => let for the base scenario (actually creating multiple memory Spaces, which is the closure idea)
for (let i = 0; i < aButtons.length; i++) {
aButtons[i].onclick = function () {
console.log('The current index is${i}`)}}Copy the code
- Custom properties (reduces the creation of execution contexts for anonymous functions that form closures)
for (var i = 0; i < aButtons.length; i++) {
aButtons[i].myIndex = i
aButtons[i].onclick = function () {
console.log('The current index isThe ${this.myIndex}`)}}Copy the code
- Custom attribute + event delegate (only need to create a function execution context, then release)
document.body.onclick = function (ev) {
var target = ev.target,
targetDom = target.tagName
if (targetDom === 'BUTTON') {
var index = target.getAttribute('index')
console.log('The current click is number${index}A `)}}Copy the code
JSBench test code
Variable localization
This improves code execution efficiency (reduces the number of paths to find when accessing data)
var i, str = ""
function packageDom() {
for (i = 0; i < 1000; i++) {
str += i
}
}
packageDom()
function packageDom() {
let str = ' '
for (let i = 0; i < 1000; i++) {
str += i
}
}
packageDom()
Copy the code
The test results
Avoid reading variables across the scope level whenever possible
Cache data
Save the data that needs to be used several times in advance and use it later
function hasClassName(ele, cls) {
console.log(ele.className)
return ele.className == cls
}
console.log(hasClassName(oBox, 'skip'))
function hasClassName(ele, cls) {
If the value of className needs to be used multiple times in the current function body, we can cache it ahead of time
var clsName = ele.className
console.log(clsName)
return clsName == cls
}
console.log(hasClassName(oBox, 'skip'))
Copy the code
- Reduce the number of statements and statements (lexical analysis, parsing)
- Cache data (scope chain lookup is faster)
Reducing access levels
function Person() {
this.name = 'zce'
this.age = 40
}
let p1 = new Person()
console.log(p1.age)
function Person() {
this.name = 'zce'
this.age = 40
this.getAge = function () {
return this.age
}
}
let p1 = new Person()
console.log(p1.getAge())
Copy the code
Anti-shake and throttling
In some high frequency scenarios, we do not want the corresponding event handler to be executed more than once
Scene:
- Scroll event
- Input fuzzy matching
- Toggle the broadcast chart
- Click on the operation
- .
Browsers have their own listening intervals by default, and detecting multiple times of listening execution can cause unnecessary resource waste
Foreground: There is a button on the screen that we can click multiple times in a row
Anti-shake: for this high frequency operation, we only want to identify one click, which can be considered as the first or last throttling: for high frequency operation, we can set the frequency ourselves, so that we will execute many times of time trigger, according to our defined frequency to reduce the number of trigger
Image stabilization
var oBtn = document.getElementById('btn')
// oBtn.onclick = function () {
// console.log(' clicked ')
// }
/** * Handle Specifies the event that needs to be executed. * Wait how long after the event is triggered. * immediate Controls whether the first or last execution is performed
function myDebounce(handle, wait, immediate) {
// Check the parameter type and handle the default value
if (typeofhandle ! = ='function') throw new Error('handle must be an function')
if (typeof wait === 'undefined') wait = 300
if (typeof wait === 'boolean') {
immediate = wait
wait = 300
}
if (typeofimmediate ! = ='boolean') immediate = false
// What we want to achieve is to have a "person" who can manage the execution times of Handle
// If we want to perform the last time, that means that no matter how many times we currently click, the previous n-1 is useless
let timer = null
return function proxy(. args) {
let self = this, init = immediate && ! timerclearTimeout(timer)
timer = setTimeout(() = > {
timer = null! immediate ? handle.call(self, ... args) :null
}, wait)
// If true is currently passed in, we need to execute immediately
// If you want to execute only on the first time, you can add a timer null as a check
// as long as the timer is Null, it means that there is no second.... Click on theinit ? handle.call(self, ... args) :null}}// Define the event execution function
function btnClick(ev) {
console.log('Hit 1111'.this, ev)
}
// When the button is clicked, it executes... The returned proxy
oBtn.onclick = myDebounce(btnClick, 200.false)
// oBtn.onclick = btnClick() // this ev
Copy the code
The throttle
// throttling: By throttling we mean letting events fire for a custom period of time
function myThrottle(handle, wait) {
if (typeofhandle ! = ='function') throw new Error('handle must be an function')
if (typeof wait === 'undefined') wait = 400
let previous = 0 // Define a variable to record the time of the last execution
let timer = null // Use it to manage timers
return function proxy(. args) {
let now = new Date(a)// Define a variable to record the time and point in time of the current execution
let self = this
let interval = wait - (now - previous)
if (interval <= 0) {
// This indicates that the operation is infrequent and can be handled
clearTimeout(timer)
timer = nullhandle.call(self, ... args) previous =new Date()}else if(! timer) {// When we find that the current system has a timer, it means that we do not need to start the timer again
// Handle should not be executed if the operation occurred within the frequency time range defined by us
// At this point, we can define a timer and ask handle to execute it after the interval
timer = setTimeout(() = > {
clearTimeout(timer) // This operation just clears the timer from the system, but the value of the timer remains
timer = nullhandle.call(self, ... args) previous =new Date()
}, interval)
}
}
}
// Define a scroll event listener
function scrollFn() {
console.log('Rolled')}// window.onscroll = scrollFn
window.onscroll = myThrottle(scrollFn, 600)
Copy the code
Reduce the level of judgment
Reduce circulatory body activity
Literals and constructors
Literals are superior to constructors