This is the 8th day of my participation in the August More text Challenge. For details, see: August More Text Challenge

The garbage collection

JavaScript is a garbage collected language, meaning that the execution environment is responsible for managing memory while the code is executing. In languages such as C and C++, tracking memory usage is a huge burden for developers and a source of many problems. JavaScript takes this burden off the developer’s shoulders, with automatic memory management for memory allocation and idle resource reclamation. The basic idea is simple: determine which variable is no longer used, and then release the memory it occupies. This process is periodic, meaning that the garbage collector runs automatically every certain time (or at some scheduled collection time during code execution). The garbage collection process is an approximate and imperfect solution, because it is undecidable whether a piece of memory is still useful. It means the algorithm can’t solve it.

Let’s take the normal life cycle of a local variable in a function. Local variables in a function exist for the duration of the function’s execution. At this point, the stack (or heap) memory allocates space to hold the corresponding value. The function uses the variable internally and then exits. At this point, the local variable is no longer needed and its memory can be freed for later use. Local variables are obviously no longer needed in this case, but it’s not always so obvious. The garbage collector must keep track of which variables will be used and which will not, in order to reclaim memory. There may be different implementations of how to mark unused variables. However, over the history of the browser, two major markup strategies have been used: tag cleanup and reference counting.

Excerpt from Chapter 4 of advanced Programming in JavaScript (4th edition)

1. Reference counting

Another less common garbage collection strategy is reference counting. The meaning of reference counting is to keep track of the number of times each value has been referenced. When a variable is declared and a reference type is assigned to it, the number of references to that value is 1. Conversely, if the variable containing the reference to this value takes another value, the number of references to that value is reduced by one. When the number of references goes to zero, the value is no longer accessible and can be reclaimed. This way, the next time the garbage collector runs, it frees the memory occupied by the values that are referenced zero times.

Reference counting has one of the biggest problems: circular references.

An 🌰 A:

Object A has A property that points to object B, and object B has A property that points to object A, and so on.

function func() { let obj1 = {}; let obj2 = {}; obj1.a = obj2; // obj1 references obj2. Obj2. a = obj1; // obj2 references obj1}Copy the code

In this example, objA and objB refer to each other through their properties; In other words, the number of references to both objects is 2. In a reference-counting strategy, since both objects are out of scope after the function is executed, objA and objB will continue to exist after the function is executed because their number of references will never be zero. Such cross-references can cause a lot of memory leaks if they exist in large numbers.

Solution: Manually dereferencing

obj1.a = null;
obj2.a = null;
Copy the code

Take 🌰 B: a circular reference problem involving COM objects:

let element = document.getElementById('some_element')
let myObject = new Object()
myObject.element = element
element.someObject = myObject
Copy the code

This example creates a circular reference between a DOM object (Element) and a native JavaScript object (myObject). The myObject variable has a property named Element that points to the DOM object Element, and the Element object has a someObject property that points back to the myObject object. Because of circular references, the MEMORY of a DOM element is never reclaimed, even if it has been removed from the page.

To avoid similar circular reference problems, you should disconnect native JavaScript objects from DOM elements without making sure they are not used. For example, you can clear the circular reference created in the previous example by:

myObject.element = null
element.someObject = null
Copy the code

Setting a variable to NULL actually cuts the relationship between the variable and its previous reference. The next time the garbage collector runs, the values are deleted and the memory is reclaimed.

Note ⚠️ : To remedy this, IE9 has changed both BOM and DOM objects to JavaScript objects. This also avoids the problems caused by having two sets of garbage collection algorithms and eliminates the common memory leaks.

2, mark and sweep

This is the most commonly used garbage collection method in javascript. When a variable enters the execution environment, it is marked as “entering the environment”. Logically, the memory occupied by variables entering the environment can never be freed, because they are likely to be used as soon as the execution flow enters the appropriate environment. When a variable leaves the environment, it is marked “out of the environment.”

When the garbage collector runs, it marks all variables stored in memory. It then removes the markup of variables in the environment and those referenced by variables in the environment. Variables tagged after that are considered to be ready to be deleted because they are no longer accessible to variables in the environment. At last. The garbage collector does the cleanup, destroying the tagged values and reclaiming the memory space they occupy.

Tag cleanup also runs into the problem of circular references. There are some objects in IE that are not native JavaScript objects. For example, the BOM and DOM objects are implemented in the form of COM (Component Object Model) objects using C++, and the garbage collector of COM objects is based on reference counting. Therefore, even though IE’s Javascript engine uses the policy of tag clearing, the COM objects accessed by Javascript are still based on the policy of reference counting. To put it bluntly, as long as Internet Explorer involves COM objects, there will be a circular reference problem.

Solution: manually disconnect the JS object from the DOM. Assign the value to null. IE9 avoids this problem by turning the DOM and BOM into real JS objects.

3. Avoid garbage collection

From the above content, although the browser can automatically perform garbage collection, but if the project is relatively large and complex code, the collection execution is costly, and in some cases even can not recognize the collection

1. Optimize arrays

Assigning [] to an array object is a shortcut to clearing an array (e.g., arr = [];). “, but note that this creates a new empty object and turns the original array object into a small piece of memory garbage! In fact, setting the length of the array to 0 (arr.length = 0) can also clear the array and reuse the array, reducing memory waste.

2. Reuse objects as much as possible

Reuse objects as much as possible, especially in places like loops create new objects, reuse them if you can. Unused objects are set to null as soon as possible and garbage collected as soon as possible.

3. Cycle optimization

Function expressions within a loop that can be reused are best placed outside the loop.

4. Avoid memory leaks

1. Unexpected global variables

function fn(arg) {
    m = "this is a hidden global variable"
}
Copy the code

M is not declared, so it becomes a global variable and will not be released until the page closes.

Another unexpected global variable can be created by this:

Function fn() {this.variable = "potential accidental global"}Copy the code

This error can be avoided by adding ‘use strict’ to the header of a JavaScript file. Enable strict mode parsing JavaScript to avoid unexpected global variables.

2. Forgotten timers or callbacks

Let someResource = getData() setInterval(function() {let node = document.getelementById (' node ') if(node)  someResource node.innerHTML = JSON.stringify(someResource)) } }, 1000)Copy the code

If the element with the id of Node is removed from the DOM, the timer will still exist, and because the callback contains a reference to someResource, the someResource outside the timer will not be released.

So remember to clear the timer duck when you’re done, and try not to reference dom objects in the timer.

3. The closure

function fn() {
    let m = document.createElement('xx')
    m.onClick = () => {
        // Even if it a empty function
    }
}
Copy the code

Closures can maintain local variables within a function so that they cannot be released.

When an event callback is defined in the previous example, the closure is formed because the function is defined within the function, and the internal function -> event callback refers to the external function

The solution:

Function fn() {let m = document.createElement('xx') m.onclick = onClickFn()} // 2. In the external function that defines the event handler, Function fn() {let m = document.createElement('xx') m.onclick = () => {// Even if it is a empty function} m = null }Copy the code

Define the event handler externally, unwrap the closure, or remove references to the DOM in the external function that defines the event handler.

4. Uncleaned DOM element references

Sometimes it’s useful to save the internal data structures of DOM nodes. If you want to quickly update a few rows of a table, it makes sense to store each row of the DOM as a dictionary (JSON key-value pairs) or an array. At this point, there are two references to the same DOM element: one in the DOM tree and one in the dictionary. When you decide to delete these lines in the future, you need to remove both references.

let elements = { button: document.getElementById('button'), image: document.getElementById('image'), text: document.getElementById('text') } function doStuff() { image.src = 'http://some.url/image' button.click() console.log(text.innerHTML) } function removeButton() { document.body.removeChild(document.getElementById('button')) // In this case, there is still a global #button reference // Elements dictionary. The button element is still in memory and cannot be reclaimed by GC. }Copy the code

Although we removeChild the button, we still have a reference to #button in the Elements object. In other words, the DOM element is still in memory.

Finally:

Tomorrow harbor performance, you can come to see Frog, work at 10:30, home at 11:30, the middle of the night to write the article.

Public number: xiao He growth, The Buddha department more text, are their own once stepped on the pit or is learned

Interested partners welcome to pay attention to me, I am: He young life. Everybody progress duck together