If useful, please also like 👍, more exciting front-end articles also please pay attention to my wechat public number [South orange front], we learn together! 😘

Preface:

We know that in JS data is stored in two forms, simple data types are stored in stack memory, and reference data types are stored in heap memory. In stack memory, garbage collection is relatively simple. When the ESP pointer is moved down, the empty space on the top of the stack is automatically reclaimed. In heap memory, garbage collection is more complicated.

How data is stored in JS:

JS data is mainly stored in four data structures: stack, heap, pool and queue

  • Stack: Simple data types, variables, addresses that reference data types

  • Heap: Reference data types, complex, and of uncertain size

  • Pool: Stores constants, such as NULL
  • Queue: Event Loop A queue of tasks in an Event Loop, characterized by first-in, first-out

The memory heap is divided into two parts:

First of all, remember that the memory heap is made up of two parts, one is called the new generation and the other is called the old generation. The new generation takes up very little space, only 32 megabytes for 64-bit operating systems. Because it is relatively small, so the data recovery efficiency is relatively high, recycling is also relatively frequent, mainly used to store the survival time is short, after the creation of objects will be recycled soon, most of the objects need to be stored here.

  

There are different garbage recycling methods for the new generation and the old generation, we introduce them separately.

New generation memory reclamation algorithm:


Inside the new generation of memory, memory is divided into two parts, one for storing data calledThe From partAnd the other part is free and calledTo part.







The recycling process is very simple, starting with the From sectionLiving objectWhat is an object of life? Objects that are reachable. In a nutshellObjects that can be found along the rootIn JS these variables are calledThe root.

  • Local variables and parameters of the current function
  • The global variable
  • Variables and arguments that are accessible on the current scope chain when a function is called in a nested manner
  • (And some internal ones)

Roots are an important concept, and the garbage collection mechanism relies on whether an object is reachable to determine whether it can be collected.

After surviving objects are found, those that are no longer alive are recycled, the surviving objects are copied To the To part, and the To part is swapped with the From part, and so on and so forth. This algorithm is the new generation garbage collection algorithm, also known asScavenge algorithm.It’s as simple as that, but are you wondering at this point why we have to split the new generation memory into two parts? And then go through the complicated operation of copy and swap? In order to preventMemory fragments.



First, after we reclaim non-viable objects in the From section, the memory should look like this, with white representing the free memory after recycling:







Visually, our memory space is very messy at this time, just like the room we haven’t cleaned up for a long time. If there is a large object to be stored at this time, it is likely that it cannot be put down intermittently due to the space occupied by the current object.



So next we copy it To the To section in that order.



  





It looks much cleaner, then swap it with the From section to continue with the other objects and repeat.



Will these objects be stored forever in the next generation? Of course not, these objects will work when there are two situationspromotion(This process is really called promotion 🤣), transfer to the older generation.

  • The current object has already undergone a Scavenge
  • To space memory ratio exceeds 25%



Old generation memory reclamation algorithm:

The old garbage collection algorithm has two kinds: counting reference algorithm, tag deletion algorithm. The former has been deprecated since IE9 due to the inability to solve the problem of looping references between objects, resulting in memory leaks, as explained here.

Memory leak refers to that the heap memory dynamically allocated in the program is not released or cannot be released due to some reason, resulting in the waste of system memory, slowing down the running speed of the program and even system crash.

Count references:

As the name implies, this is to record the number of times an object is referenced, mark it as 1 when it is referenced, subtract one mark when a reference is deleted, and automatically reclaim the object when it is marked as 0.

var a = new Object(a);// the reference count for 'this object' is 1.
var b = a; // The reference count for 'this object' is 2 (a,b)
a = null; // reference_count = 1
b = null; // reference_count = 0 
// The next GC is to reclaim 'this object'
Copy the code

While this is an “ancient” approach, it has many advantages: an object can be reclaimed as soon as it is marked 0, there is no need to recursively search for surviving objects like the new generation, and there is minimal impact on the main thread execution code. The disadvantages are obvious: there is no way to solve the problem of circular references, which can take up a lot of memory if every referenced object is marked.

Tag deletion algorithm:

Most browsers have since adopted the tag deletion algorithm, which is also very simple:

  1. First find all the “roots” and mark (remember) them
  2. It then iterates (using breadth first) and marks all references from them
  3. And then walk through those references again and mark them. All marked objects are remembered to avoid double marking.
  4. Finally, until all reachable objects are marked, delete those that are not marked


  





We can see it clearly in this picturereferencedDoesn’t mean thatUp to the, this is a good solution to the problem of circular reference.



After deleting the unmarked objects, the same problem occurs, and we still have a lot of memoryMemory fragmentsTo solve this problem we need to move all objects to one end neatly “close together”.



  



This process is also one of the most time-consuming parts of garbage collection, since we need objects and may go through objects copying and so on.



Garbage collection impact on performance:

As we know, JS is a single-threaded language. When we do garbage collection, whether in the new generation space or the old generation space, we should suspend the execution logic of the main thread and continue to execute the garbage collection after completion. This process is called total pause.

Taking 1.5g garbage collection heap memory as an example, V8 takes more than 50ms to do a small garbage collection, and even more than 1s to do a non-incremental garbage collection. This is the amount of time that JavaScript threads pause during garbage collection, and application performance and responsiveness plummets with this amount of time.

Therefore, we find that garbage collection still has a great impact on the performance of JS language. For the new generation space, the memory occupied by it is relatively small and the time used is relatively small, but for the old generation space, the memory occupied by it is relatively large, and the garbage collection part has to go through the marking, deletion and sorting caused by the total pause time is also relatively large. So in order to improve the performance of the JS language and avoid too long total pauses, we have the following optimizations:

Limit the size of operable memory:

For Java and the language is the memory size of the operating system is basically no limit, but we also know that JS language is single-threaded, if memory is too big, so recycling used the more, this is obviously not very good, so we in the V8 engine operable memory size for the limit.

  • 64-bit operating system: 1.4 GB in total, 32 MB for the new generation
  • 32-bit OS: total allocation: 0.7 GB, 16 MB for the new generation

Incremental collection:

A complete garbage collection is time-consuming, so we divide the garbage collection into multiple times, a little bit each time, interspersed in the JS main thread code execution process, so that the total pause time is also effectively reduced. This process is mainly used in the marking phase of the old generation space. Each part of the marking is paused, and the logic of other applications is executed for a period of time, and then another part is marked until all the reachable objects are marked. V8’s successor also introduced Lazy sweeps and Incremental Compaction to make cleanings and reoperations Incremental. It is also planned to introduce parallel tagging and parallel collation, further utilizing multi-core capabilities to reduce each pause time. In Javascript, garbage collection is automatically triggered, unlike C, where you need to manually free memory via malloc() and free().

  • If the while/for loop ends, local variables or objects created within it are recycled
  • Local variables created in a function scope that are not referenced will be recycled after the function is executed

What we really need to be aware of here are those cases where garbage collection cannot be actively triggered. In these cases, the memory occupied by the data cannot be garbage collected and will remain in memory, affecting the performance of the page and causing a lag until the page is closed. This is also known as a memory leak.

All of this theory is for practical purposes, and the nature of our research on garbage collection is to improve the performance of our pages and reduce memory leaks.

Examples of memory leaks:

1. Forget to declare local variables

This should be common on new hands, where a variable we assign inside a function that is not declared becomes a global variable and cannot be reclaimed until the page closes.

function a(){
    b=2
    console.log('B is not declared! ')}Copy the code

2. The closure

An anonymous function creates a variable that should be recycled at the end of its execution but cannot be recycled because it is called by an internal function. If you want to learn more about closures, check out my article.

var leaks = (function(){
    var leak = 'xxxxxx';// a reference in a closure is not recycled
    return function(){
        console.log(leak);
    }
})()
Copy the code

3. Forget to remove temporary values when removing DOM nodes

Many times, for performance reasons, we will fetch multiple nodes at once and store them in an object. If we remove the node from the page later, but the reference in the object remains, this situation will also cause memory leaks.

var element = {
  image: document.getElementById('image'),
  button: document.getElementById('button')};document.body.removeChild(document.getElementById('image'));
// If element is not reclaimed, it is useless to remove the image node. The image node is still in memory.
Copy the code

Similarly, if we bind events to a node and then delete the node, memory leaks will occur if the node is not unbound.

let oDiv = document.querySelector('div');
        oDiv.onclick = function(){
            alert(111111111)}document.body.removeChild(oDiv);
oDiv.onclick = null; // Unbind events to trigger garbage collection
Copy the code

4. The memory of the timer leaks

var someResource = getData();
setInterval(function() {
    var node = document.getElementById('Node');
    if(node) {
        node.innerHTML = JSON.stringify(someResource)); }},1000);
someResource = null; // The timer is still referencing variables that cannot be collected
Copy the code

If we do not delete the timer, the data referenced by the someResource in the timer will always exist and cannot be recycled, but when the timer expires, the objects referenced in the callback function will still be recycled, so there are special considerations for extremely long timers.

These four examples, undeclared variables inside functions, closures, object references to nodes that are not removed after deletion, and timer references to data, are important to remember. Here’s how to check for memory leaks in Chrome.

How to view in Chrome:

Finally, some tips on how to check for memory leaks in Chrome:

  1. First, press F12 to open the browser’s console
  2. Click on the Performance
  3. Check theScreenshots 和 memory
  4. Click on the dot ⚪ in the upper left corner to start recording
  5. Click Stop to stop recording

We can see in the figure that the Heap increases periodically, rising to a point and then falling, which is garbage collection. The interval between the two poles is the garbage collection cycle. If the minimum value keeps increasing, there is an obvious memory leak, as shown in the figure.

Finally:

More exciting front-end articles also please pay more attention to my wechat public number [South orange front] we study together, come on together!! If you think the content is good, please also like 👍👍👍

References:

Javascript. Info “Garbage Collection” Shen Yuan “V8 Engine garbage Memory Collection Principle analysis” Mint front end “Javascript garbage Collection and Memory Leakage” EdmundChen “V8 Memory Allocation and Garbage Collection” Nan Xiao 忎 “V8 Engine garbage Collection mechanism”