This article is dedicated to the author's personal understanding and summary of the garbage collection mechanism of the V8 engine. This article mainly refers to the article to understand the garbage collection of the V8 engine.
Before understanding the V8 garbage collection mechanism, let's first explain some concepts:
「全停顿」:垃圾回收算法在执行前,需要将应用逻辑暂停,执行完垃圾回收后再执行应用逻辑。
If a GC takes 50ms, the application logic will pause for 50ms. Why stop there?
①Because js is executed in a single thread, after entering the garbage collection, the js application logic needs to be suspended to leave space for the garbage collection algorithm to run.
② Garbage collection is actually a very time-consuming operation.
V8 engine garbage collection strategy:
- The garbage collection strategy of V8 is mainly based on the generational garbage collection mechanism , which performs different generations of memory garbage collection according to the survival time of objects , and then uses different garbage collection algorithms for different generations.
- The Scavenge algorithm is mainly used in the garbage collection process of the new generation; the Mark-Sweep (mark removal) and Mark-Compact (mark finishing) algorithms are used in the old generation.
Before understanding the garbage management algorithms of the new generation and the old generation, let's take a look at the memory structure of the garbage management of the V8 engine;
Memory structure of V8 engine garbage management:
- New generation (new_space) : Most of the objects will be allocated here. This area is relatively small but garbage collection is very frequent. This area is divided into two halves, one half is used for memory allocation, and the other half is used for garbage collection. Copy the objects you want to keep.
(The author has seen two partitions: ①From area: To area = 1:1 ②From area: To area: To area = 8:1:1, this article is only used to understand the recycling mechanism, and I will not discuss it too much here) - Old generation (old_space) : Objects in the new generation will be transferred to the old generation memory area after surviving for a period of time, and the garbage collection frequency of this memory area is lower than that of the new generation. The old generation is further divided into the old generation pointer area and the old generation data area. The former contains most objects that may have pointers to other objects, and the latter only saves original data objects, which do not have pointers to other objects.
- Large object area (large_object_space) : stores objects whose size exceeds the size of other areas. Each object will have its own memory, and garbage collection will not move the large object area.
- Code area (code_space) : The code object will be allocated here, the only memory area with execution permission.
- Map area (map_space) : Stores Cell and Map, each area stores elements of the same size, and has a simple structure.
New generation area:
The new generation area is mainly implemented by the Scavenge algorithm, which divides the new generation area into an active area (new space), also called From area, and an inactive new space, also called To area .
The objects of life in the program will be stored in the From space. When the new generation is garbage collected, the surviving active objects in the From area will be copied to the To area for storage, and then the objects in the From will be recycled and put The roles of the From space and the To space are swapped, that is, the To space will become the new From space, and the original From space will become the To space.
Therefore, the algorithm is an algorithm that sacrifices space for time.
Based on the above algorithm, the algorithm diagram is implemented as follows (reproduced):
- Suppose we have allocated three objects A, B, C in the From space
- When the main thread task of the program is executed for the first time and enters garbage collection, it is found that object A has no other references, it means that it can be recycled
- Object B and Object C are still active at this time, so they will be copied to the To space for saving
- Next, all non-surviving objects in the From space are cleared
- At this point, the memory in the From space has been emptied, and a role swap is completed with the To space.
- When the main thread of the program is executing the second task, a new object D is allocated in the From space
- After the task is executed, it enters garbage collection again, and finds that object D has no other references, indicating that it can be recycled
- Image B and object C are still active at this time, and are copied to the To space again for saving
- Clear all non-surviving objects in the From space again
- From space and To space continue to complete a role swap
Object promotion:
When an object is still alive after multiple copies, it will be considered as an object with a long life cycle. When the next garbage collection is performed, the object will be directly transferred to the old generation. The process of moving to the old generation is called promotion .
There are two main conditions for object promotion (one of them can be satisfied):
- Whether the object has undergone a Scavenge algorithm
- Whether the memory ratio of the To space has exceeded 25%
By default, the objects we create will be allocated in the From space. When garbage collection is performed, before copying the object from the From space to the To space, the memory address of the object will be checked to determine whether it has experienced a Scavenge algorithm. , if the address has changed, the object will be transferred to the old generation and will not be copied to the To space.
The flow chart means:
If the object has not undergone the Scavenge algorithm, it will be copied to the To space, but if the memory ratio of the To space has exceeded 25% at this time, the object will still be transferred to the old generation, as shown in the following figure:
The reason why there is a 25% memory limit is because the To space will complete the role swap with the From space after going through the Scavenge algorithm once, and it will become the From space. Subsequent memory allocations are performed in the From space. If it is too high or even overflows, it will affect the allocation of subsequent objects, so after this limit is exceeded, the objects will be directly transferred to the old generation for management.
Old generation area:
Before explaining the old-generation Mark-Sweep (mark clearing) and Mark-Compact (mark sorting) algorithms, let's review the reference counting method : for object A, any object references the value of A, the counter is +1, and the reference is invalid. Counter -1, blame recycling when the counter is 0, but there will be a case of circular references, which may lead to memory leaks, since 2012, all modern browsers have abandoned this algorithm.
function foo() {//循环引用样例
let a = {};
let b = {};
a.a1 = b;
b.b1 = a;
}
foo();
Mark-Sweep algorithm:
Mark-Sweep (mark clearing) is divided into two phases: marking and clearing. In the marking phase, all objects in the heap will be traversed, and then the living objects will be marked. In the clearing phase, the dead objects will be cleared. The Mark-Sweep algorithm mainly knows whether an object should be recycled by judging whether an object can be accessed. The specific steps are as follows:
- The garbage collector will internally build a root list, which is used to start from the root node to find those variables that can be accessed. For example, in JavaScript, the window global object can be seen as a root node.
- The garbage collector starts from all root nodes, traverses the child nodes that it can access, and marks them as active. The places that the root node cannot reach are inactive and will be regarded as garbage.
- The garbage collector will free all inactive memory blocks and return them to the operating system.
However, the memory space after marking and clearing will generate a lot of discontinuous fragment space. In this discontinuous fragment space,
Large objects may not be stored due to insufficient space.
In order to solve the problem of memory fragmentation, another algorithm is needed: Mark-Compact.
Mark-Compact:
Marking and tidying treats non-surviving objects instead of reclaiming them immediately, but moving the live objects to one side, and then directly clearing the memory beyond the end boundary.
Here, for ease of understanding, two flowcharts are cited.
- Suppose there are four objects A, B, C, and D in the old generation
- During the marking phase of garbage collection, object A and object C are marked as live
- During the cleanup phase of garbage collection, move live objects to one end of the heap memory
- During the cleanup phase of garbage collection, all the memory on the left side of the active object is reclaimed
So far, the whole process of the old generation garbage collection has been completed. However, due to the existence of the "full pause" mentioned above, the execution of the main thread will also be hindered in the marking phase. Generally speaking, the old generation will save a large number of surviving objects. If the entire heap memory is traversed during the marking phase , it will inevitably cause serious freezes. Therefore, the V8 engine has introduced the concept of Incremental Marking.
Incremental Marking :
Change the operation that originally required a one-time traversal of the heap memory to the method of incremental marking. First mark a part of the objects in the heap memory, then pause, and hand over the execution right to the JS main thread again. Marking continues at the point where marking was paused until the entire heap is marked.
That is: divide the large task of garbage collection into small tasks, and execute them in the middle of JavaScript tasks.
This concept is actually a bit like the Fiber architecture in the React framework. Only in the idle time of the browser will it traverse the Fiber Tree to perform the corresponding tasks, otherwise the execution will be delayed, affecting the tasks of the main thread as little as possible, avoiding application lag, improving application performance.
Thanks to the benefits of incremental marking, the V8 engine continued to introduce lazy sweeping and incremental compaction, making the process of cleaning and sorting incremental. At the same time, in order to make full use of the performance of multi-core CPUs, parallel marking and parallel cleaning will also be introduced to further reduce the impact of garbage collection on the main thread and improve more performance for applications.
Finally, the trigger mechanism of V8-GC is attached:
References and image sources:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。