This is the fifth day of my participation in the August More text Challenge. For details, see:August is more challenging

For the front-end siege, the JS memory mechanism can not be ignored. If you want to become an industry expert or build high-performance front-end applications, you must understand the memory mechanisms of JavaScript

Look at the chestnuts

    function foo (){
        let a = 1
        let b = a
        a = 2
        console.log(a) / / 2
        console.log(b) / / 1
        
        let c = { name: 'the nuggets' }
        let d = c
        c.name = 'MuHua'
        console.log(c) // {name: 'mu'}
        console.log(d) // {name: 'mu'}
    }
    foo()
Copy the code

As you can see, the results are a little different after we change the values for different data types.

This is because different data types are stored in different locations in memory. In the JS execution process, there are mainly three types of memory space: code space, stack, heap

Code space is mainly about storing executable code. There is a lot of information about this, so see another article of mine for more details

Let’s look at stacks and heaps first

The stack and heap

In JS, each piece of data requires one memory space. What are the distinguishing features of different memory Spaces? As shown in figure

Call stack is also called execution stack, its execution principle is first in, then out, the last to execute will first out of the stack, as shown in the figure

The stack:

  • Storage Base Types:Number, String, Boolean, null, undefined, Symbol, BigInt
  • Storage and usageLast in, first out(Like a bottle, what goes in last comes out first)
  • Automatically allocates memory space, automatically releases, occupies a fixed size of space
  • Stores a variable of a reference type, but actually stores not the variable itself, but something that points to the objectPointer to the(Address in heap memory)
  • All variables defined in a method are stored on the stack, and when the method is finished, the stack of the method is automatically destroyed
  • Methods can be called recursively, so that JVW maintains a long trace of method calls as the stack deepens, causing stack overflows due to insufficient memory allocation

Pile:

  • Storage reference type:The Object (Function/Array/Date/RegExp)
  • Dynamically allocates memory space that is variable in size and does not automatically free
  • Objects in the heap are not destroyed at the end of the method because they may be referenced by another variable (parameter passing, etc.)

Why are there stacks and heaps

Usually related to garbage collection mechanisms. Each method creates its own stack, and the variables in the method are placed into the stack one by one. When the method is executed, the stack is automatically destroyed

In order to minimize the amount of memory the program uses to run, the stack space is not set to be too large, while the heap space is very large

Every time an object is created, the object is saved to the heap for reuse, and even after the method completes, the object is not destroyed because it may be referenced by another variable (parameter passing, etc.) until the object has no reference at all and is not destroyed by the system’s garbage collection mechanism

In addition, JS engine needs to use the stack to maintain the context state during the execution of the program. If all the data is in the stack, if the stack space is large, the efficiency of context switch will be affected, and then the execution efficiency of the entire program will be affected

Memory leaks and garbage collection

JS allocates memory when creating variables (objects, strings, etc.) and “automatically” frees memory when they are no longer used. This automatic memory free process is called garbage collection. Because of the existence of garbage collection, many developers do not care much about memory management during development, which in some cases leads to memory leaks

Memory life cycle:

  1. Memory allocation: When we declare variables, functions, and objects, the system automatically allocates memory for them
  2. Memory usage: reading and writing memory, that is, using variables, functions, parameters, etc
  3. Memory reclamation: The garbage collection mechanism automatically reclaims the memory that is no longer used

A local variable (a variable inside a function) is reclaimed when the function finishes and there are no other references (closures)

The lifetime of global variables does not end until the browser unloads the page, which means they are not garbage collected

Memory leaks

The running of a program requires memory. For a continuously running service process, the memory that is no longer used must be released in a timely manner. Otherwise, the memory usage becomes larger and larger

A memory leak is a waste of memory caused by an oversight or error that causes a program to fail to release memory that is no longer used

Determining memory leaks

In Chrome, you can view memory usage this way

Developer Tools => Performance => Select Memory => Click the upper left corner Record => Page operation and click Stop

The memory usage during this time is then displayed

  1. If the current memory usage trend is on the rise after you view the memory usage, you can conclude that memory leaks exist
  2. If the memory usage increases, you can also consider that memory leaks exist

In Node, use the process.memoryUsage method to view memory

console.log(process.memoryUsage());
Copy the code
  • HeapUsed: the part of the heapUsed.
  • RSS: Resident set size (RSS) : Indicates all memory usage, including instruction area and stack.
  • HeapTotal: The amount of “heap” memory, both used and unused.
  • External: memory occupied by C++ objects in the V8 engine

The heapUsed field is used to determine memory leaks

Under what circumstances does a memory leak occur

  1. An undeclared global variable that was accidentally created
  2. The timer and callback functions are forgotten, and the reference to the timer will remain in memory if it is not closed in time
  3. closure
  4. DOM manipulation references (for example, if td is referenced but the entire table is deleted, the entire table is retained in memory)

How can memory leaks be avoided

So remember a rule: if you don’t use something, return it in time

  1. Reduce unnecessary globals, such as using strict patterns to avoid creating unexpected globals
  2. Reduce the number of long-lived objects and avoid too many objects
  3. Dereferencing data in a timely fashion (variables in closures,DOM references, timer cleanup) after using the data
  4. Organize the logic to avoid endless loops that cause browsers to stall and crash

The garbage collection

JS has an automatic garbage collection mechanism. How does it work?

Reclaim data from the execution stack

See the chestnuts

function foo(){
    let a = 1
    let b = { name: 'MuHua' }
    function showName(){
        let c = 2
        let d = { name: 'MuHua' }
    }
    showName()
}
foo()
Copy the code

Implementation process:

  • The JS engine first creates an execution context for the foo function and pushes it onto the execution stack
  • The execution encounters the showName function, creates an execution context for the showName function, and pushes the execution context onto the stack, so that the showName is pressed above the foo in the stack
  • The JS engine has a pointer to record the current execution state (ESP), which will point to the executing context, that is, showName
  • When the showName execution is complete, the process moves to the next execution context, which is the function foo, and the showName execution context needs to be destroyed. The JS engine moves the ESP pointer down to the execution context below showName, i.e. Foo. This move is the process of destroying the execution context of showName

As shown in figure

Reclaim the data in the heap

The idea is to find values that are no longer in use and free up memory.

For example, the context of foo and showName is cleared, but the two objects still occupy space, because the data of the objects is stored in the heap. Only the reference address of the object is cleared, not the data of the object

This is where the garbage collector comes in

The most difficult task in the garbage collection phase is to find the variables you don’t need, so there are many different garbage collection algorithms, and no one will work for all scenarios. You need to weigh your options according to the scenarios

Reference counting

Reference counting is an old garbage collection algorithm that defines “memory no longer used” by simply looking at whether an object has a reference to it. If no other object points to it, the object is no longer needed

But it has a fatal problem: circular references

If two objects reference each other, even though they are no longer in use, garbage collection will not collect them, resulting in a memory leak

To solve the problem caused by circular references, modern browsers do not use reference counting

In V8, the heap is divided into two areas: the new generation and the old generation

The new generation and the old generation

V8 implements the GC algorithm and adopts the generational garbage collection mechanism, so V8 divides the heap memory into the new generation (the secondary garbage collector) and the old generation (the main garbage collector)

The new generation

In the Cenozoic era, the storage capacity ranges from 1 to 8 meters. Therefore, the storage capacity is mainly for short-lived objects

Exploiture GC algorithm is used in the Cenozoic era to divide the Cenozoic space into two regions: the object region and the idle region. As shown in figure:

As the name implies, only one of these two Spaces is used and the other is free. Here’s how it works

  • The newly allocated objects are stored in the object region, and when the object region is full, the GC algorithm is started
  • The garbage in the object area is marked. After the mark is completed, the surviving objects in the object area are copied to the free area, and the unused objects are destroyed. This procedure leaves no memory fragments
  • After the copy is complete, the object area is interchanged with free. It recycles the garbage and allows the new generation to reuse these two areas indefinitely

And because there’s not a lot of space in the new generation, there’s a tendency to fill up, so

  • Objects that have survived two garbage collections are moved to the old generation space
  • If the proportion of free space objects exceeds 25%, the objects will be moved to the old generation space in order not to affect memory allocation

The old generation

The old generation is characterized by large space occupancy, so it mainly stores objects with a long life

In the old generation, the tag clearing algorithm and the tag compression algorithm are used. Be insane. Copy a large object if you use the Scavenge GC algorithm

Mark clear

The marker clearing algorithm is started first in the following cases:

  • When a space is not partitioned
  • The number of objects exceeds the space limit
  • Space does not guarantee the transfer of objects from the new generation to the old generation

The process of tag clearance goes like this

  • Starting from the root (js global objects), we walk through all the objects in the heap, and then mark the surviving objects
  • After the marking is complete, the unmarked objects are destroyed

Because of the garbage collection phase, the JS script execution is suspended and then resumed after the garbage collection is complete. This behavior is called stop-the-world.

For example, if there is more than 1 gb of data in the heap, a complete garbage collection may take more than 1 second, during which time the JS thread will be suspended, resulting in poor page performance and responsiveness

Incremental tag

So in 2011, V8 switched from the stop-the-world flag to the incremental flag. Using the incremental marking algorithm, GC can decompose the collection task into a number of small tasks, interlaced with JS tasks, so as to avoid the application lag

Concurrent tags

Then, in 2018, there was another major breakthrough in GC technology: concurrent tagging. Allow JS to run simultaneously when GC scans and tags objects

Mark compression

When the fragmentation exceeds a certain limit, the flag compression algorithm is started to move the living objects to one end of the heap. When all the objects are moved, the memory that is not needed is cleared

conclusion

Praise support, hand stay fragrance, and have glory yan

Thanks for seeing this, come on!

reference

How browsers work and practice