This article is based on JavaScript’s Memory Management Explained. Written with additional information. If you want to learn directly from the text, you can ignore this article. But don’t forget to like + follow.
If you feel ok, please like more, encourage me to write more wonderful article 🙏.
If you have any questions, feel free to comment in the comments section
How to run a series in a browser
- JS (Event Loop) and Call Stack (Call Stack)
- JS memory Management
The profile
- 1. Memory life cycle
- 2.Stack and heap
- Stack: Static memory allocation
- Heap: Dynamic memory allocation
- The sample
- 3.References in JS
- The sample
- 4.The garbage collection
- Reference counting
- Mark clear
- 5.A memory leak
- Mount variables to global variables
- Turn off the timer and clear the callback function
- Clearing DOM references
Memory life cycle
In JS, whenever a variable, function, or other data structure is created, the JS engine silently does a lot for us — allocate memory for the corresponding data structure and deallocate/release it when it is no longer needed.
If you are familiar with the underlying language (C/C++), you can allocate and recycle memory manually in C++ by using the standard library allocate/Deallocate
Allocating memory is the process of reserving memory, while reclaiming memory is the process of reclaiming occupied memory to provide space for other operations.
Just like people living and dying, there are also memory correspondingThe life cycle. Whenever a variable is assigned or a function is created, memory goes through the following process:
- distributionmemory
JS engine
Allocate the corresponding space for the objects we need to manipulate - Reading and writing to memory using memory is just reading and writing to variables
- recyclingmemory
JS engine
Through specificalgorithmDetermines whether memory needs to be freed to provide space for other operations.
Objects in the memory manager include not only JS objects, but also functions and function scopes.
Everything is an object in JS
Stack and heap
As we know from above, the JS engine allocates space and reclaims it when it is not referenced.
So where exactly is this allocated space stored?
The JS engine has two places to store data: memory heap and stack.
Heap and stack are two different data structures in which the JS engine stores data.
Stack: Static memory allocation
Above, all values are stored in stack –> Since they are of type primitive.
Stack is the JS data structure used to store static data. Static data is a type of data whose size the JS engine knows exactly at compile time. In JS, static data including primitive types (string/number/Boolean/undefined/null) and a reference type (pointer to the object and function).
Since the JS engine knows that their size does not change, it allocates them a specified amount of memory each time.
The process of allocating memory before code execution is called static memory allocation.
Because the JS engine allocates a fixed amount of memory for these data, the data stored in the stack has a memory ceiling. The upper limit is determined by each browser.
Heap: Dynamic memory allocation
Heap is where JS holds objects and functions.
Unlike stack, the JS engine does not allocate a fixed amount of memory for these objects. Instead, these Spaces are allocated on demand.
This way of allocating memory is called dynamic memory allocation.
To help distinguish the difference between stack and heap, draw the following table:
The difference between | Stack | Heap |
---|---|---|
Storage type | Primitive and reference types | Objects and functions |
Memory is a negative value | inCompilation phaseThe memory size has been determined | inOperation phaseTo determine the size |
Check whether there is a memory upper limit for storing data | Yes (Rules vary by browser) | There is no memory upper limit |
The sample
const person = {
name: 'John'.age: 24};Copy the code
JS allocates space in the heap for this object. However, the value of the attribute is primitive, so the attribute value is stored in the stack.
const hobbies = ['hiking'.'reading'];
Copy the code
An array is also an object, so it is also stored in the heap.
let name = 'John'; // Allocate memory for string
const age = 24; // Allocate memory for numbers
name = 'John Doe'; // Reallocate memory
const firstName = name.slice(0.4); // Reallocate memory
Copy the code
Raw data are immutable. To replace the original value, JS creates a new value.
References in JS
All variables have specified information in the stack. As we saw above, stack stores two types of values – primitive and reference. Primitive types need not be explained too much, whereas reference types need more explanation.
Since objects and functions are stored in the heap, which is a messy data structure with no specified order, the JS interpreter compiles code from top to bottom in the order in which the data appears, in the order in which the data is stored in the specified location by data type. When data is found to be non-primitive, the address of the reference stored in the heap is stored in the stack.
The garbage collection
From the previous study, we already know how the JS engine stores different data, but everything has a beginning and a end, since there is memory allocation, there will be memory reclamation.
As with memory allocation, memory reclamation is done for us. And a garbage collector is in charge.
Once the JS engine finds that a variable or function is no longer referenced, the GC deallocate/release the space it occupies.
The main problem with reclaiming memory is that it is unpredictable whether or not it will be referenced, which means there is no algorithm that can reclaim all memory when it is not occupied.
Let’s discuss some typical garbage collection algorithms.
Reference counting
This is the easiest way to do it. It does that by recycling thoseNo pointerObject to which.
Through the above series of operations, though will
person
and
newPerson
References to are set to
null
Object, but
hobbies
Is still referenced by other variables.
A circular reference
For reference counting GC, there is a case where you can’t handle a circular reference. When one or more objects refer to each other but are already isolated. At this point, the reference count gets confused.
let son = {
name: 'John'};let dad = {
name: 'Johnson',
}
son.dad = dad;
dad.son = son;
son = null;
dad = null;
Copy the code
Although both son and dad are set to null when not in use. This is just to separate them from the references in the stack, but in the heap, they also refer to each other.
Son and DAD objects refer to each other, and the reference-counting algorithm cannot free memory if the two objects are not referenced by other objects.
Mark clear
Mark clearing algorithm can solve the pain point of circular reference well. Unlike reference counting, which counts the number of references to an object to determine whether or not to garbage collect, it uses a method of traversing from the root object. If an object is not traversed, the corresponding memory needs to be freed by GC.
In the browser, the root object is the Window object, and in NodeJS, the global object.
The algorithm passes
tagObjects that cannot be accessed are the targets of the GC.
The root element does not participate in the tagging process!
A memory leak
With the introduction above and the understanding of existing concepts, let’s analyze some common possible causes of memory leaks.
Mount variables to global variables
Unscrupulous storage of data in global variables is a very common memory leak.
In the browser environment, if var/const/let is used by default when defining a variable, the variable will be mounted to the Window object.
users = getUsers();
Copy the code
We can run the code in strict mode.
If you have to mount a variable to a global variable under certain circumstances, you need to manually set the variable to NULL when its mission is complete.
window.users = null;
Copy the code
Turn off the timer and clear the callback function
Forgetting to clear timers and callback functions will make your project code bigger and bigger. Especially for spAs (single-page applications), be careful when adding timers and callbacks dynamically.
Turn off timer in time
const object = {};
const intervalId = setInterval(function() {
// everything used in here can't be collected
// until the interval is cleared
doSomething(object);
}, 2000);
Copy the code
The above code is executed every 2m, and the variables in the timer cannot be GC until the timer is turned off.
Therefore, when the timer is not in use, it needs to be destroyed.
clearInterval(intervalId);
Copy the code
Clear the callback function
const element = document.getElementById('button');
const onClick = () = > alert('hi');
element.addEventListener('click', onClick);
element.removeEventListener('click', onClick);
element.parentNode.removeChild(element);
Copy the code
Clearing DOM references
Storing DOM elements in JS can also cause memory leaks.
When you want to destroy an element through an array, this situation will not trigger a GC operation
const elements = [];
const element = document.getElementById('button');
elements.push(element);
function removeAllElements() {
elements.forEach((item, index) = > {
document.body.removeChild(document.getElementById(item.id));
elements.splice(index, 1);
});
}
Copy the code