1. Memory structures for basic data types

First, a quick overview of JavaScript; For the difference between Undefined and Null, there are many online introduction, in this article is not involved, if you have doubts, please go to the relevant blog.

Some observers may ask why we should first introduce the memory structure of JavaScript’s basic data types before introducing the JavaScript memory model. This is because the JavaScript memory model is as close to the memory structure of the primitive data types as mathematics is to the real numbers, which underlie the entire JavaScript memory model.

So let me explain the memory structure of the basic data types in the shortest possible way. Memory structure for basic data types: In JavaScript basic data types are stored in memory as values. Here’s an example:

var inta = 10;
var strb = 'Hello';
Copy the code

So after executing this JavaScript code, there are two areas in memory that are represented as INTA, STRB; Where, the value of inTA region is’ 10 ‘, and the value of STRB region is’ Hello ‘, that is, the actual truth values of inTA and STRB memory region are saved.

2. Memory structures that reference data types

In JavaScript, there are only reference data types left, so it’s worth introducing reference data type memory structures after playing with basic data types., JavaScript and Java are similar in that they cannot directly access the heap memory, so they both use “reference” to access objects in the heap. The relationship between reference and object can be described as the relationship between remote control and TV. We can control the TV by holding the remote control. The so-called reference is actually the address of a block of memory, that is, the memory address value of the object in memory is stored in the area representing the reference, as shown in the figure:

Suppose the object is located in a region in memory named 0x23215, then the region of the ellipse represents the reference to the object, and the value 0x23125 is stored in the ellipse. In the actual operation, the execution environment will find the object in memory through the 0x23125 stored in the reference.

3. Memory model

3.1 Memory Classification

At JavaScript execution time, memory can be logically divided into two parts: stack and heap.After context generation is performed, a variable object (described in a future article) is created, which is a special object that is also stored in the heap. Whereas primitive data types tend to be stored directly in variable objects, reference data types actually store in variable objects the address of a reference to the object (the reference itself).

After studying the memory model in JavaScript, take a look at the following code and guess its output to verify your understanding of the above knowledge:

var inta = 10;
var stra = 'Hello';
 
var obja = {a: 10, b: 'Hello'};
 
var intb = inta;
var strb = stra;
var objb = obja;
 
intb = 20;
strb = 'World';
objb.a = 20;
objb.b = 'World';
 
console.log(inta, intb);
console.log(stra, strb);
console.log(obja, objb);
Copy the code

Running results:



10 20
Hello World
{ a: 20, b: 'World' } { a: 20, b: 'World' }
Copy the code

This involves assignment of objects:

  • Whenever an assignment is made to a primitive data type, the original value is assigned to a new object, so changing the value of the new object does not affect the original value (because they essentially hold two values).
  • Assigning to a reference data type assigns the address of the object to which the reference refers to to another reference. Later, changing the internal value of the object with the new reference will still affect the object to which the reference refers (because they essentially hold the same object).

3.2 Variable declaration and assignment

  1. The essence of variable declarations is that variable names are bound to stack memory addresses, not directly to heap memory.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. Complex data type assignment is to apply for a new memory region in the heap to store the value, and then apply for a new memory region in the stack to store the value of the memory address that it points to and bind its memory address in the stack to the variable.
  7. The assignment statement simply points two variables to the same stack memory address

3.3 Deep and Shallow Replication

The complex data types mentioned above point to the same heap memory space through Pointers,

  • 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.

4. Memory reclamation mechanism

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.

4.1 Two modes of garbage collection

4.1.1 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.

// Create an object with the variable user pointing to two attributes of the object. Var user = {name:'liuyunshengsir',
    handsome: true}; User. name = null; // Name = null; var s = user; // We modify and release the user reference to the object, but the variable s still has a reference user = null; // The variable s is no longer referenced, and the object is soon released by the garbage collector.Copy the code

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. Even after the function completes execution, the garbage collector cannot free it by reference counting.

4.1.2 Mark clearing Algorithm (reachability)

The concept of mark clearing is easy to understand, starting from the root to see if an object can be achieved, if it can be achieved, the object is still needed, if not, then release it, this process is roughly divided into three steps:

  1. The garbage collector creates a list of roots. Roots are usually global variables that retain references in code. In JS, we generally consider the global object Window as root, the so-called root.

  2. All roots are checked from the root, all children are recursively checked, and those that can reach from root are marked as active.

  3. Data not marked as active is determined to be no longer needed, and the garbage collector starts releasing it.

When an object has zero references, we cannot reach it from the root; On the other hand, things that cannot be reached from the root are not necessarily strictly zero references, such as circular references, so mark clearing is better than reference counting.

Since 2012, all modern browsers have used the tag sweep garbage collection algorithm, except older versions of Internet Explorer 6.

JavaScript engine optimization for garbage collection:

  • Generation reclamation: Objects are divided into new objects and old objects. When new objects appear, they are cleaned up. Objects that have existed for a long time are seldom checked
  • Incremental collection: Decompose garbage collection into multiple parts and perform them separately
  • Idle time collection: The garbage collector only runs when the CPU is idle

4.2 Defects of garbage collection

Javascript’s GC strategy is not immune to the problem that GC stops responding to other operations for safety reasons.

5. How to avoid memory leaks

Now that we know how garbage collection works, how can we avoid the embarrassment of creating objects that cannot be collected and causing a memory leak? Here are four common types of JS memory leaks.

5.1 Global Variables

It is common sense for JS developers to create global variables as little as possible, but there are two ways to accidentally create global variables:

function fn() {
    a = 1;
};
fn();
window.a //1
Copy the code

In the above code, we declared a variable a inside the function body. Since we did not use the var declaration, it is still a global variable even inside the function body. We know that a global variable is equivalent to adding properties to the window, so we can still access it after the function completes execution.

:


function fn() {
    this.a = 1;
};
fn();
window.a //1

Copy the code

We know that when we call fn directly, it is equivalent to window.fn(), so this inside the function will point to the window, so we are essentially creating a global variable.

Try adding ‘use strict’ to the header. You’ll notice that A is not accessible, because in strict mode, the global object points to undefined.

Sometimes we can’t avoid using global variables, so remember to release them manually after using them, such as null.

5.2 The Forgotten timer

var serverData = loadData();
setInterval(function () {
    var renderer = document.getElementById('renderer');
    if(renderer) { renderer.innerHTML = JSON.stringify(serverData); }}, 3000);Copy the code

In the code above, when the DOM element renderer is removed, the timer callback function can never be recycled because it is a periodic timer. This also causes the timer to keep referring to the data serverData. It is good practice to stop the timer when it is not needed.

5.3 Forgotten listening events

When we use event listeners, for example, remember to remove listener events if they are no longer needed.

var element = document.getElementById('button');

function onclick(event) {
    element.innerHTML = 'text';
};

element.addEventListener('click', onclick); / / remove the monitor element. RemoveEventListener ('click', onclick);
Copy the code

5.4 the closure

var theThing = null;
var replaceThing = function () {
    var originalThing = theThing;
    var unused = function() {//unused, but a reference to theThing remainsif (originalThing)
            console.log("hi"); }; // Create a new object theThing = {longStr: new Array(1000000).join(The '*'),
        someMethod: function () {
            console.log("message"); }}; };setInterval(replaceThing, 1000);
Copy the code

Each time the timer calls replaceThing, theThing gets a new object containing the array longStr and the closure someMethod.

The unused closure preserves a reference to originalThing and, because theThing is assigned, a reference to theThing. Although unused, a reference relationship causes originalThing to remain unreclaimed, so does theThing. It is correct to add originalThing = null at the end of replaceThing;

So we often say that for variables in closures, always remember to release them manually when you don’t need them.

5.5 DOM References

Manipulating the DOM is always considered bad, but it has to be done, and the convention is to store it in a variable so it can be used over and over again, but this also creates a problem, the DOM is referenced twice.

var elements = document.getElementById('button')

function doStuff() {
    elements.innerHTML = 'To hear the wind is the wind'; }; Elements = null; document.body.removeChild(document.getElementById('button'));

Copy the code

In the code above, one reference is to the DOM tree, and the second reference is to the variable Elements, which we clean up twice when we don’t need it.

5.6 Circular Reference

A circular reference cannot be cleared by the tag

thinking

All local variables should be declared as const or let. The default is const, unless the variable needs to be reassigned. The var keyword cannot be used.

Although it is not specified why, as far as I know, there are several reasons:

  • Eliminate bugs in advance.
  • Variables declared with const must be initialized at declaration time. This forces programmers to consider scope when declaring variables, which ultimately benefits memory management and performance.
  • When communicating with others who touch the code, the code can speak for itself: which variables are immutable (as far as JS is concerned) and which variables can be reassigned.