In JavaScript data types are divided into basic types (number/string, Boolean/undefined/null/symbol/bigInt) and a reference type (Object), why want to distinguish between data types?
Because they’re stored differently in memory.
Memory model in JavaScript
/ / the first line
let a = 0
/ / the second line
let objA = {
name: 'jill'
}
/ / the third row
console.log(a)
console.log(objA)
......
Copy the code
When the program runs to the first line of code, it will apply for a block of memory in stack memory, address A1, create a unique identifier a for variable A, form a mapping relationship with memory address A1, and store the value of A in A1, as shown in the following figure
When the program runs to the second line of code, it will apply for a block of memory in stack memory, address A2, and then apply for a block of memory in heap memory to store objA’s value address H1, and store the address H1 in A2, as shown in the following figure:
When the program runs to the third line of code, it reads the value of A from memory and prints it out.
When the program reaches the fourth line of code, it reads the value of A2 from memory, finds the value of H1, and prints it.
ObjA can reclaim the allocated Spaces A1, A2, and H1 if A is not subsequently used.
This is the memory life cycle:
Allocate memory → Use memory → Reclaim memory
Suppose the following code followed:
/ / in the fourth row
let objB = {
name: 'peter'
}
/ / the fifth row
objA = objB
Copy the code
When the program runs to the fourth line of code, it will apply for a block of memory in the stack (address A3), and then apply for a block of memory in the heap (address H2 of objA), and store the address of H2 in A3
At line 5, the program changes the address in A2 to H2. The diagram below:
At this point, H1 has no way to access it, which is called “garbage”. Suppose there are a hundred, a thousand, ten thousand…… Such unreachable objects occupy memory, and subsequent programs cannot apply for memory, resulting in memory leaks….. The “garbage” H1 is to be collected.
V8 Memory limits
JavaScript is typically run in the browser, and the recycling mechanism is similar, using the V8 engine as an example.
On 32-bit systems, V8 has 0.7GB of memory; On 64-bit systems, V8 has 1.4GB of memory.
So why is memory limited?
First, JavaScript was designed to run only as a scripting language on the browser side, and it was not likely to use a lot of memory.
JavaScript garbage collection, on the other hand, is not synchronized with JavaScript code runtime, meaning that JavaScript pauses while garbage collection is being done. If the memory is too large, garbage collection takes too long, which affects the user experience.
Common garbage collection algorithms
First, let’s look at common garbage collection algorithms:
Reference counting
-
Core idea: Set the number of references to determine whether the current number of references is 0
-
Reference calculator
-
The reference relationship change is to modify the reference number
-
Reclaim immediately if the reference number is 0
Advantages of reference counting algorithm:
-
Recycle garbage when it is found
-
Minimize program pauses
Disadvantages of reference counting algorithm:
-
Object referenced by loop cannot be recycled
-
High time overhead: Reference counting requires maintenance of reference changes and constant monitoring of reference changes
Implementation principle of tag clearing
-
Core idea: mark and clear two stages to complete
-
Traverse all objects to find mark active object (reachable object)[Phase 1]
-
Iterate through all objects to remove unmarked objects and remove all marks [Stage 2]
-
Reclaim space
Advantages and disadvantages of tag clearing:
-
Disadvantages: space fragmentation
-
Advantages: Solves the problem that circular references cannot be recycled
-
Garbage is not collected immediately
Principle of tag collation algorithm
-
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 the cleanup, moving the object
Advantages: Reduced space fragmentation does not immediately recycle garbage
Garbage collection mechanism
Garbage Collector (GC)
V8 divides memory into two parts, the new generation memory space and the old generation memory space:
New generation memory space: Used to store objects with short lifetime. The size is 16M on 32-bit systems and 32M on 64-bit systems. The Cenozoic era is divided into semispace(From) and Semispace (To).
Old generation memory space: Used to store objects that live for a long time. The size is about 700 MB on a 32-bit system and 1400 MB on a 64-bit system.
Cenozoic recovery process
The New generation is divided into two large Spaces, semispace(From) and Semispace (To). From space is in use, and To space is idle. When we allocate objects, we allocate them first in the Form space, and when garbage collection is done, we use the mark-up algorithm To copy living objects To the To space, while non-living objects are freed. When you’re done, you swap the From space with the To space. The above recycling algorithm is called the Scavenge algorithm.
Objects that survive multiple copies can be moved to the old generation store, a phenomenon known as promotion.
Promotions also occur when 25% of the To space is used.
The recycling process of old generation objects
Old-generation storage uses a marker clearing algorithm, which first iterates through all objects in the heap and marks the surviving objects. The unmarked objects are then cleared. However, the tag clearing algorithm will lead to space fragmentation, so the tag clearing algorithm will be used to sort out the old generation storage space after the clearing.
The Scavenge, tag scavenging, and tag collation algorithms require application logic to be suspended while running and recycled, which is known as the “total freeze.” In garbage generation collection, the default configuration of the new generation is small and there are fewer surviving objects. However, the old generation configuration is large, many live objects, total pause will cause a great impact.
V8 followed with incremental tagging, which is to break the tagging process into several smaller processes that are interspersed.
Efficient use of memory
Now that you know JavaScript’s garbage collection mechanism, what can you do to make it work more efficiently while writing code?
It is divided into the following aspects:
1. Actively release variables
If it is a variable defined on a global object, the global scope releases the object until the program exits, causing the referenced object to reside in memory (in the old generation).
The following is an example:
// In the browser environment
this.foo = 1 // This points to the window
window.addEventListener('keydown', keydownFun) // Bind events globally
let obj = {name: 'jill'}
let timer = setTimeout(() = >{
let a = 1
let b = obj
// Variables used in timers A, B, obj are not recycled
}, 10) // Create timer is not destroyed
Copy the code
For the above situation, we need to take the initiative to release variables after using them, and we will timely recycle the next garbage.
delete this.foo // Using delete to delete release has an impact on V8 optimization. The following is recommended
this.foo = undefined // reassign and release
window.removeEventListener('keydown', keydownFun) // Unbind global events after use
clearTimeout(timer) // Actively destroy the timer
timer = null // Unreference the timer
Copy the code
Do closures cause memory leaks? Don’t closures pop up the call stack and stay in memory?
In fact, there is a bug in IE. After the closure is used, IE still cannot recycle the variables used in the closure. But this is an IE problem, not a closure problem.
When you create a closure, you create a global variable that will be recycled when you’re done using it.
function foo() {
let a = 1
function bar () {
console.log(a);
}
return bar ;
}
let testBar = foo();
testBar(); / / 1
testBar = null; // No longer used in testBar, reassign it
Copy the code