Garbage Collection strategy

Garbage collection has two strategies: manual collection and automatic collection.

Manual reclamation requires you to control the allocation and destruction of memory. If the allocated memory is not destroyed after use, memory leaks will occur.

JavaScript, on the other hand, uses a different strategy of automatic collection through the garbage collector.

Because the data in JavaScript is stored in the stack (raw data type) and the heap (reference data type), its garbage collection also includes both stack garbage collection and heap garbage collection.

Garbage collection in the stack

In the following code, we can first see how the garbage in the callback stack is collected.

function fn() {
    let num1 = 1;
    let obj1 = {test: "haha"};
    function fn2() {
        let num2 = 2;
        let obj2 = {test: "xixi"};
    }
    fn2();
}
fn();
Copy the code

When fn2 is executed, the state of the call stack and heap is shown below

As you can see from the figure, data of primitive types is allocated on the stack, and data of object reference types is allocated on the heap.

When fn2 is finished, the stack corresponding to fn2 is destroyed. How is it destroyed?

There is a current execution state pointer ESP on the stack, which points to the context in which the function is currently executing. When Fn2 finishes executing, the pointer is moved down to fn’s execution context, which is the process of destroying fn2’s execution context.

If another function is executed later, the execution context of that function is overridden directly in place of the original fn2 execution context.

When a function finishes executing, the JS engine destroys its execution context on the stack by moving ESP down.

Garbage collection in the heap

After the stack is reclaimed, let’s look at how the heap is reclaimed.

To recycle garbage data from the heap, you need a JavaScript garbage collector.

V8 divides the heap into two regions: the new generation, which stores short-lived data, and the old generation, which stores long-lived data.

For the sake of efficiency, the new area is generally small, with a capacity of 1-8 M, while the old area has a much larger capacity. The two zones use different garbage collectors. The new generation of garbage is collected through the secondary garbage collector, while the old generation of garbage is collected through the main garbage collector.

The garbage collector workflow

The garbage collector workflow is pretty much the same.

The first step is to mark the objects in the space. The objects in the space are divided into active objects and inactive objects. Inactive objects are the objects that can be garbage collected.

The second step is to reclaim memory occupied by inactive objects. After all the marks have been completed, all objects marked as inactive in memory are cleaned up.

The third step is to defragment memory. After garbage collection, there may be discontinuous space in memory. If large contiguous memory needs to be allocated later, then these discontinuous space may not be allocated, so we need to defragment the memory. The defragmentation step is not necessary, however, because some garbage collectors, such as the paralgarbage collector we’ll cover below, do not defragment memory when collecting.

Secondary garbage collector

The secondary garbage collector is responsible for garbage collection in the new area. Except for objects that occupy a large amount of memory, most objects are allocated to the newborn area, so the garbage collection in the newborn area is more frequent than that in the old area.

The secondary garbage collector is processed using the Scavenge algorithm. The algorithm divides the space of the new area in half, with one as the area for storing objects and the other as the free area.

New objects are placed in the object area, and when the object area is nearly full, garbage collection is performed.

Deputy garbage collector of garbage in object area first, after the completion of the tag will be active objects in an orderly fashion is copied to the free area, so in the process of rubbish also equivalent to finished the sorting of memory, copy after the free region becomes the object region, while the original object area into a spare area. This completes the garbage collection.

Garbage collection is done whenever the object area is nearly full, so the object area and free area are also flipped over and reused indefinitely.

So there’s a problem, there’s not a lot of space in the freshman area, what if it gets filled up? To solve this problem, the V8 engine uses an object promotion strategy, where if an object survives two garbage collections, it is moved to the old area.

Main garbage collector

From the introduction of the garbage collector, we can see that the objects in the old area are mainly the objects that occupy relatively large memory and the objects that live for a long time, and the objects that occupy large memory are directly allocated to the old area.

Because the old area objects are large, if the same collection strategy as the garbage collector is adopted, the replication operation will take a long time, and half of the space will be wasted, so the main garbage collector uses another algorithm, mark-collation algorithm for garbage collection.

We start with a set of root elements and recursively traverse the set of root elements. Objects that can be traversed are marked as active objects, and objects that can’t be traversed are marked as inactive data that needs to be garbage collected.

After marking, all live objects are moved to the same side. After the move, all memory outside the end boundary is cleaned up, which completes memory collation and garbage collection.

Incremental marking algorithm

Because JavaScript is single-threaded, no other tasks can be performed while garbage collection is being performed, so garbage collection that takes too long can cause significant lag.

Because the space of the new area is small, so the impact is relatively small, we can ignore, while the old area because of its large space, so the garbage recycling time may be too long.

In order to reduce the lag caused by garbage collection in old areas, V8 divides the marking process in garbage collection into a sub-marking process, and at the same time allows the garbage collection marking and JavaScript logic to be executed alternately until the marking stage is completed. This algorithm is called incremental marking algorithm.

By breaking up a large task into smaller tasks, you can keep garbage collection and JavaScript logic running smoothly without affecting the user experience.