preface
Recently in the overall review of modern front-end essential core knowledge points, will be organized into a front-end analysis summary article series. This article is the second one, mainly summarizes the JS underlying memory model. (This series of articles can also be viewed in the Language Sparrow – Core Front-end series).
This article is originally from dino notes, reprinted by 😁
Data types and memory
Data type classification
It is mainly divided into two categories: basic data type and complex data type, which are classified as follows.
Basic data types | String, Number, Boolean, Null, Undefined, Symbol |
---|---|
Complex data types | Object and all types that inherit from Object |
There are different memory areas for storing data for different data types, with basic data types stored directly in stack memory and complex data types stored in heap memory.
Memory classification
The memory classification in JS is related to the JS engine, which is usually V8 engine in browsing. The main purpose of memory differentiation is garbage collection. For example, in V8 garbage collection mechanism, different garbage collection algorithms are adopted according to the new generation and old generation memory to ensure garbage collection efficiency.
JS memory space is divided into stack memory and heap memory. Stack memory is a stack structure that stores basic data types and Pointers to heap memory, while heap memory stores complex data types.
Variable declaration and assignment
Core point summary
-
The essence of variable declarations is that variable names are bound to stack memory addresses, not directly to heap memory.
-
Declared basic data types store values in stack memory, and declared complex data types store values in heap memory and their memory address in the heap as values in stack memory.
-
A const declaration constant essentially means that the stack memory address to which the declared variable name refers cannot be changed, but the corresponding stack value can be changed.
-
Basic data type assignment is to apply for a new memory area in stack memory to hold the value and bind the memory address it points to to the original variable.
-
Complex data type assignment is to apply for a new memory region in the heap and store the value and take the memory address that it points to and apply for a new memory region in the stack and store it and bind its memory address in the stack to a variable.
Explain variable declarations and assignments
Basic data types
let index = 23
Copy the code
Base data types store values directly in stack memory and bind variables to the corresponding address of the value in the stack.
let _index = index
Copy the code
Declaring another variable, _index, and assigning it to index, essentially binds the _index and index variables to the memory address that index points to.
index = 45
Copy the code
Changing the value of index to the basic data type is actually allocating memory in stack memory and binding the resulting memory address to the variable index.
Memory in the figure refers to Stack Memory, the same as Stack in the figure below
Complex data types
let students = []
Copy the code
When a complex data type is declared, it allocates memory space in the heap to store its value, and stores the address of the allocated heap memory space in the stack memory as the value. The variable is directly bound to the memory address on the stack.
Modify complex data by reference
let _students = students
_students.push({ name: 'Ming' })
Copy the code
The _status = STUDENTS assignment statement simply points two variables to the same stack memory address, and the push() statement allocates new space in the heap to store the new array and store its heap address on the stack.
A more complicated example
let obj = { name: 'Ming' }
let arr = []
arr = [ obj ]
obj = null
Copy the code
The schematic diagram of the memory model is as follows:
[obj] = {index:’ Ming ‘}; [obj] = {index:’ Ming ‘}; [obj] = {index:’ Ming ‘};
Detail constant declarations and assignments
The procedure for declaring a basic data type as a constant is the same as the procedure for declaring a basic data type
const index = 1
index = 100 // TypeError: Assignment to constant variable.
Copy the code
Assignment to a constant declared as a primitive data type results
Error binding index variable to newly generated memory address: constant bound memory address is not allowed to change.
The procedure for declaring complex data types as constants is the same as the procedure for declaring basic data types.
const students = []
students = [{ name: 'little red' }]
Copy the code
Assignment to a constant declared as a complex data type yields the following result
Error binding students variable to newly generated memory address: constant bound memory address is not allowed to change.
Deep replication and shallow replication
The complex data types mentioned above refer to the same heap memory space through Pointers. The main difference between deep and shallow copies is whether the heap memory space is allocated to hold a copy of the original value.
For object or array types, when we assign a to B and then change the properties in B, A changes as well. That is, a and B refer to the same block of memory, so changing any value in it will change the other value. This is a shallow copy (copy).
Deep copy (copy) is in the process of a to b given a new heap memory space to store a copy of value, at the same time in the presence of nested properties of complex data type (recursive traversal) should also be in the same way, the final copy new data from the object under any level of the complex object has a new heap memory to store the corresponding value.
No further details here, please refer to this article.
Three, garbage recycling
If there is memory, there must be garbage collection (GC). Most of the stack memory in JS is used when the function is executed (also called the call stack according to the order of function call), and the stack memory garbage collection begins after the function is executed. Heap memory needs to be processed by a specific garbage collection algorithm due to the existence of multiple Pointers in stack memory and large heap memory.
The key to garbage collection is how do you determine that memory is no longer being used and then free it up
Reference counting algorithm
It is mainly used by Internet Explorer and other old browsers to analyze the number of references to variables through counters and clear the variables that are not referenced. Circular references cannot be handled, for example:
function cycle() {
var o1 = {}
var o2 = {}
o1.a = o2
o2.a = o1
return "Cycle reference!"
}
cycle()
Copy the code
O1 refers to O2 and O2 refers to o1. After the cycle function is executed, o2 is not referenced again, but the reference counting algorithm determines that there are references in both.
Scavenge algorithm
For V8 generation memory, split generation memory into two parts: From and To, garbage collection is completed during conversion between From and To.
Mark clearing algorithm
A cleanup algorithm for heap memory used in early V8, where the heap is scanned globally to find unused objects to mark and eliminate, since memory fragmentation occurs without defragmenting.
Tag sorting algorithm
Global scan heap memory to find out unused objects while sorting and cleaning, solve the problem of memory fragmentation caused by mark cleaning algorithm.
Incremental clearing, sorting
The heap memory size is generally large, so the whole heap needs to be scanned when the previous algorithms are used for garbage collection, resulting in the suspension of JS execution logic for a long time. Incremental clear, clean is to split the mark clear or mark clean into a step, alternately execute JS logic and a step, the maximum less JS execution logic pause time.
🌰 Example Analysis
For example, run the following code from a browser console (ES6 syntactic compatibility aside for now).
const fn = (arr) = > {
const _arr = arr
return _arr.join()
}
const index = 0
const array = new Array(20).fill(1)
fn(array)
Copy the code
Variable declarations (executed in sequence)
The stack memory storage constant integer index, heap memory storage array array
Calling a function (function call stack)
The variables arr and _arr point to array and store the string returned by join into stack memory
Clear the function call stack
The variables arr and _arr in the function call stack belong to the function scope and will be cleared if they are not accessible.
Clear stack memory
The string returned by fn, index, array, and fn will all be cleared.
If you do not exit the console after executing the JS logic in the browser console, the above stack clearing will be performed after shutting down the console.
Analysis of common problems
The 🌰 closure causes memory leaks
A closure extends the scope of a function in JS by indirectly giving outsiders access to the variables inside the function by returning a function.
Create closures as follows:
const generateFn = (a)= > {
const obj = { index: 1 }
return (a)= > {
return obj
}
}
Copy the code
const obj2 = generateFn()() // Obj2 now points to obj defined above
Copy the code
Memory leaks are caused by the following usage: I
window.fn = generateFn() // The returned function is bound globally, with no active cleanup
Copy the code
GenerateFn generates a function that refers to obj and binds it to the global object Window so that FN cannot be recycled, and fn references obj so that obj cannot be recycled, resulting in a memory leak.
🌰Weak reference of WeakSet and WeakMap
WeakSet and WeakMap are two new data structures in ES6, and their references to values are not counted in the garbage collection mechanism. WeakSet can only store non-repeating objects, while WeakMap can only store key-value pairs with objects as keys. When an object becomes inaccessible, its storage record is automatically lost.
WeakMap analysis
Examine the following code to illustrate the nature of weak references.
let obj = { index: 0 }
let vs = new WeakSet(a)
vs.add(obj)
obj = null // Clear obj to clear {index: 0} objects during GC
Copy the code
Add obJ object into WeakSet instance, at this time, obJ object can be queried through obj and VS variable call, obj=null to clear the object, at this time, the reference of OBJ object in VS will also be automatically cleared.
After the GC is complete (you can still see the OBj object by printing directly console.log, because the GC is not complete), you can see that the VS items are empty.
In addition, in the process of reading React source code, it is found that domEventListenerMap. js has practical application of WeakMap. In the next article, the practical application of WeakMap and WeakSet will be studied in depth.
Write in the last
Now that I see this, I might as well click a “like” to encourage the author
Author’s blog: blog.lessing.online
Author: github github.com/johniexu
[Comprehensive analysis and summary of front-end series]
- Comprehensive analysis and summary of BFC principle and practice
Refer to the article
- JavaScript memory mechanism (prerequisite for advanced students)
- Help you jump ship after the New Year and get a raise, in-depth understanding of JavaScript memory models
- Ruan Yifeng – JavaScript memory leak tutorial
- Node.js memory model
- In-depth understanding of JavaScript execution context, function stack, and promotion concepts
- Deep understanding of JavaScript closures