preface

Recently in the overall review of modern front-end essential core knowledge points, will be organized into a front-end analysis summary article series. This article is the second one, mainly summarizes the JS underlying memory model. (This series of articles can also be viewed in the Language Sparrow – Core Front-end series).

This article is originally from dino notes, reprinted by 😁

Data types and memory

Data type classification

It is mainly divided into two categories: basic data type and complex data type, which are classified as follows.

Basic data types String, Number, Boolean, Null, Undefined, Symbol
Complex data types Object and all types that inherit from Object

There are different memory areas for storing data for different data types, with basic data types stored directly in stack memory and complex data types stored in heap memory.

Memory classification

The memory classification in JS is related to the JS engine, which is usually V8 engine in browsing. The main purpose of memory differentiation is garbage collection. For example, in V8 garbage collection mechanism, different garbage collection algorithms are adopted according to the new generation and old generation memory to ensure garbage collection efficiency.

JS memory space is divided into stack memory and heap memory. Stack memory is a stack structure that stores basic data types and Pointers to heap memory, while heap memory stores complex data types.


Variable declaration and assignment

Core point summary

  • The essence of variable declarations is that variable names are bound to stack memory addresses, not directly to heap memory.

  • Declared basic data types store values in stack memory, and declared complex data types store values in heap memory and their memory address in the heap as values in stack memory.

  • A const declaration constant essentially means that the stack memory address to which the declared variable name refers cannot be changed, but the corresponding stack value can be changed.

  • Basic data type assignment is to apply for a new memory area in stack memory to hold the value and bind the memory address it points to to the original variable.

  • Complex data type assignment is to apply for a new memory region in the heap and store the value and take the memory address that it points to and apply for a new memory region in the stack and store it and bind its memory address in the stack to a variable.

Explain variable declarations and assignments

Basic data types

let index = 23

Copy the code

Base data types store values directly in stack memory and bind variables to the corresponding address of the value in the stack.

let _index = index

Copy the code

Declaring another variable, _index, and assigning it to index, essentially binds the _index and index variables to the memory address that index points to.

index = 45

Copy the code

Changing the value of index to the basic data type is actually allocating memory in stack memory and binding the resulting memory address to the variable index.

Memory in the figure refers to Stack Memory, the same as Stack in the figure below

Complex data types

let students = []

Copy the code

When a complex data type is declared, it allocates memory space in the heap to store its value, and stores the address of the allocated heap memory space in the stack memory as the value. The variable is directly bound to the memory address on the stack.

Modify complex data by reference

let _students = students

_students.push({ name: 'Ming' })

Copy the code

The _status = STUDENTS assignment statement simply points two variables to the same stack memory address, and the push() statement allocates new space in the heap to store the new array and store its heap address on the stack.

A more complicated example

let obj = { name: 'Ming' }

let arr = []

arr = [ obj ]

obj = null

Copy the code

The schematic diagram of the memory model is as follows:

[obj] = {index:’ Ming ‘}; [obj] = {index:’ Ming ‘}; [obj] = {index:’ Ming ‘};

Detail constant declarations and assignments

The procedure for declaring a basic data type as a constant is the same as the procedure for declaring a basic data type

const index = 1

index = 100 // TypeError: Assignment to constant variable.

Copy the code

Assignment to a constant declared as a primitive data type results


Error binding index variable to newly generated memory address: constant bound memory address is not allowed to change.

The procedure for declaring complex data types as constants is the same as the procedure for declaring basic data types.

const students = []

students = [{ name: 'little red' }]

Copy the code

Assignment to a constant declared as a complex data type yields the following result


Error binding students variable to newly generated memory address: constant bound memory address is not allowed to change.

Deep replication and shallow replication

The complex data types mentioned above refer to the same heap memory space through Pointers. The main difference between deep and shallow copies is whether the heap memory space is allocated to hold a copy of the original value.

For object or array types, when we assign a to B and then change the properties in B, A changes as well. That is, a and B refer to the same block of memory, so changing any value in it will change the other value. This is a shallow copy (copy).

Deep copy (copy) is in the process of a to b given a new heap memory space to store a copy of value, at the same time in the presence of nested properties of complex data type (recursive traversal) should also be in the same way, the final copy new data from the object under any level of the complex object has a new heap memory to store the corresponding value.

No further details here, please refer to this article.

Three, garbage recycling

If there is memory, there must be garbage collection (GC). Most of the stack memory in JS is used when the function is executed (also called the call stack according to the order of function call), and the stack memory garbage collection begins after the function is executed. Heap memory needs to be processed by a specific garbage collection algorithm due to the existence of multiple Pointers in stack memory and large heap memory.

The key to garbage collection is how do you determine that memory is no longer being used and then free it up

Reference counting algorithm

It is mainly used by Internet Explorer and other old browsers to analyze the number of references to variables through counters and clear the variables that are not referenced. Circular references cannot be handled, for example:

function cycle() {

var o1 = {}

var o2 = {}

o1.a = o2

o2.a = o1

return "Cycle reference!"

}

cycle()

Copy the code

O1 refers to O2 and O2 refers to o1. After the cycle function is executed, o2 is not referenced again, but the reference counting algorithm determines that there are references in both.

Scavenge algorithm

For V8 generation memory, split generation memory into two parts: From and To, garbage collection is completed during conversion between From and To.


Mark clearing algorithm

A cleanup algorithm for heap memory used in early V8, where the heap is scanned globally to find unused objects to mark and eliminate, since memory fragmentation occurs without defragmenting.


Tag sorting algorithm

Global scan heap memory to find out unused objects while sorting and cleaning, solve the problem of memory fragmentation caused by mark cleaning algorithm.


Incremental clearing, sorting

The heap memory size is generally large, so the whole heap needs to be scanned when the previous algorithms are used for garbage collection, resulting in the suspension of JS execution logic for a long time. Incremental clear, clean is to split the mark clear or mark clean into a step, alternately execute JS logic and a step, the maximum less JS execution logic pause time.


🌰 Example Analysis

For example, run the following code from a browser console (ES6 syntactic compatibility aside for now).

const fn = (arr) = > {

const _arr = arr

return _arr.join()

}

const index = 0

const array = new Array(20).fill(1)

fn(array)

Copy the code

Variable declarations (executed in sequence)

The stack memory storage constant integer index, heap memory storage array array


Calling a function (function call stack)

The variables arr and _arr point to array and store the string returned by join into stack memory


Clear the function call stack

The variables arr and _arr in the function call stack belong to the function scope and will be cleared if they are not accessible.


Clear stack memory

The string returned by fn, index, array, and fn will all be cleared.


If you do not exit the console after executing the JS logic in the browser console, the above stack clearing will be performed after shutting down the console.

Analysis of common problems

The 🌰 closure causes memory leaks

A closure extends the scope of a function in JS by indirectly giving outsiders access to the variables inside the function by returning a function.

Create closures as follows:

const generateFn = (a)= > {

const obj = { index: 1 }

return (a)= > {

return obj

}

}

Copy the code
const obj2 = generateFn()() // Obj2 now points to obj defined above

Copy the code

Memory leaks are caused by the following usage: I

window.fn = generateFn() // The returned function is bound globally, with no active cleanup

Copy the code

GenerateFn generates a function that refers to obj and binds it to the global object Window so that FN cannot be recycled, and fn references obj so that obj cannot be recycled, resulting in a memory leak.

🌰Weak reference of WeakSet and WeakMap

WeakSet and WeakMap are two new data structures in ES6, and their references to values are not counted in the garbage collection mechanism. WeakSet can only store non-repeating objects, while WeakMap can only store key-value pairs with objects as keys. When an object becomes inaccessible, its storage record is automatically lost.

WeakMap analysis

Examine the following code to illustrate the nature of weak references.

let obj = { index: 0 }

let vs = new WeakSet(a)

vs.add(obj)

obj = null // Clear obj to clear {index: 0} objects during GC

Copy the code

Add obJ object into WeakSet instance, at this time, obJ object can be queried through obj and VS variable call, obj=null to clear the object, at this time, the reference of OBJ object in VS will also be automatically cleared.

After the GC is complete (you can still see the OBj object by printing directly console.log, because the GC is not complete), you can see that the VS items are empty.

In addition, in the process of reading React source code, it is found that domEventListenerMap. js has practical application of WeakMap. In the next article, the practical application of WeakMap and WeakSet will be studied in depth.

Write in the last

Now that I see this, I might as well click a “like” to encourage the author

Author’s blog: blog.lessing.online

Author: github github.com/johniexu


[Comprehensive analysis and summary of front-end series]

  • Comprehensive analysis and summary of BFC principle and practice

Refer to the article

  • JavaScript memory mechanism (prerequisite for advanced students)
  • Help you jump ship after the New Year and get a raise, in-depth understanding of JavaScript memory models
  • Ruan Yifeng – JavaScript memory leak tutorial
  • Node.js memory model
  • In-depth understanding of JavaScript execution context, function stack, and promotion concepts
  • Deep understanding of JavaScript closures