Garbage Collection and Memory Leakage in JavaScript

  Front end, html, javascript, node.js, Programmer

Preface

The program needs memory to run. As long as the program requests, the operating system or runtime must supply memory. The so-called memory leak is simply memory that is no longer used and is not released in time. In order to better avoid memory leakage, we first introduce Javascript garbage collection mechanism.

In languages such as C and C++, developers can directly control the application and recycling of memory. However, in Java, C#, JavaScript languages, the application and release of variables’ memory space are handled by the program itself, and developers do not need to care. In other words, Javascript has an automatic Garbage Collecation mechanism.

If you want to read more excellent articles, please stamp them fiercely.GitHub blogFifty excellent articles a year are waiting for you!

First, the necessity of garbage collection

The following passage is quoted from the JavaScript Authoritative Guide (4th Edition)

Since strings, objects, and arrays do not have fixed sizes, they can only be dynamically allocated when their sizes are known. Every time a JavaScript program creates a string, array, or object, the interpreter must allocate memory to store that entity. As long as the memory is allocated dynamically like this, the memory will eventually be released so that they can be reused. Otherwise, the JavaScript interpreter will consume all available memory in the system, causing the system to crash.

This passage explains why the system needs garbage collection. Unlike C/C++, JavaScript has its own garbage collection mechanism.

The mechanism of JavaScript garbage collection is very simple: find out the variables that are no longer used, and then release the memory that they occupy. However, this process is not always, because its cost is relatively large, so the garbage collector will periodically execute it at regular intervals.

Var a = "sailing in the waves";
 Var b = "front-end craftsman";
 var a = b;  //rewrite a

After this code runs, the string “Sailing in the Waves” has lost its reference (previously referenced by A). After the system detects this fact, it will release the storage space of the string so that the space can be reused.

Second, the garbage collection mechanism

How does the garbage collection mechanism know which memory is no longer needed?

There are two ways to recycle garbage:Mark Clear, Reference Count. Reference counting is less common, and mark clearing is more common.

1. Mark removal

This is the most common 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 released, because they may be used as long as the execution stream enters the corresponding environment. When a variable leaves the environment, it is marked as “leaving the environment.”

The garbage collector marks all variables stored in memory when it is running. Then, it removes variables in the environment and tags referenced by variables in the environment. Variables tagged after this will be considered as variables to be deleted because the variables in the environment are no longer accessible. Finally The garbage collector completes the memory cleaning, destroys those marked values, and reclaims the memory space they occupy.

Let’s use an example to explain this method:

Var m = 0,n = 19 // mark m,n,add () as entering the environment.
 Add(m, n) // marks a, b, c as entering the environment.
 Log (n)//a, b, c is marked as leaving the environment and waiting for garbage collection.
 function add(a, b) {
 a++
 var c = a + b
 return c
 }

2. Reference Count

The so-called “reference count” means that the language engine has a “reference table” that keeps the number of references of all resources (usually various values) in memory. If the number of references to a value is 0, it means that the value is no longer used, so this memory can be freed.


In the above figure, the two values in the lower left corner have no reference, so they can be released.

If a value is no longer needed and the number of references is not 0, the garbage collection mechanism cannot release this memory, resulting in memory leakage.

var arr = [1, 2, 3, 4];
 arr = [2, 4, 5]
 Log ('sailing on the waves');

In the above code, the array [1, 2, 3, 4] is a value that takes up memory. The variable arr is the only reference to this value, so the number of references is 1. Although arr is not used in the following code, it will continue to occupy memory. As for how to free up memory, we will introduce below.

In the third line of code, the variable arr referenced by the array [1, 2, 3, 4] obtains another value, and then the number of references of the array [1, 2, 3, 4] is reduced by 1. At this time, its number of references becomes 0, which indicates that there is no way to access this value again, thus the memory space occupied by the array [1,2,3,4] can be reclaimed.

But there is one big problem with reference counting: circular references

function func() {
 let obj1 = {};
 let obj2 = {};
 
 obj1.a = obj2;  // obj1 refers to obj2
 obj2.a = obj1;  // obj2 refers to obj1
 }

When the function func finishes executing, the return value is undefined, so the entire function and the internal variables should be recycled, but according to the reference counting method, the number of references of obj1 and obj2 are not 0, so they will not be recycled.

To solve the problem of circular references, it is best to manually set them to null when they are not used. The above example can do this:

obj1 = null;
 obj2 = null;

Three, what circumstances will cause memory leaks?

Although JavaScript will automatically collect garbage, if our code is not written properly, the variable will always be in the state of “entering the environment” and cannot be recycled. Here are some common cases of memory leaks:

1. Unexpected global variable

function foo(arg) {
 bar = "this is a hidden global variable";
 }

Bar is not declared and will become a global variable and will not be released until the page is closed.

Another unexpected global variable may bethisCreate:

function foo() {
 this.variable = "potential accidental global";
 }
 // foo calls itself, this points to the global object (window)
 foo();

Adding’ use strict’ to the header of JavaScript files can prevent such errors. Enable strict mode parsing JavaScript to avoid unexpected global variables.

2. Forgotten Timer or Callback Function

var someResource = getData();
 setInterval(function() {
 var node = document.getElementById('Node');
 if(node) {
 //handle node and someResource
 node.innerHTML = JSON.stringify(someResource));
 }
 }, 1000);

Such code is very common. If the element with id Node is removed from DOM, the timer will still exist. Meanwhile, because the callback function contains a reference to someResource, someResource outside the timer will not be released.

Closure

function bindEvent(){
 var obj=document.createElement('xxx')
 obj.onclick=function(){
 // Even if it is a empty function
 }
 }

Closures can maintain local variables within functions so that they cannot be released. When defining the event callback in the above example, the closure is formed because the function is defined within the function and the internal function-event callback refers to the external function.

//Define event handlers outside
 function bindEvent() {
 var obj = document.createElement('xxx')
 obj.onclick = onclickHandler
 }
 //or delete the reference to dom in the external function that defines the event handler function
 function bindEvent() {
 var obj = document.createElement('xxx')
 obj.onclick = function() {
 // Even if it is a empty function
 }
 obj = null
 }

The solution is to define the event handler externally, remove the closure, or delete the reference to dom in the external function that defines the event handler.

4. No clean DOM element references

Sometimes, it is useful to save the internal data structure of DOM nodes. If you want to quickly update the contents of several rows of a table, it makes sense to save each row of DOM into a dictionary (JSON key-value pair) or array. At this point, there are two references to the same DOM element: one in the DOM tree and the other in the dictionary. When you decide to delete these lines in the future, you need to clear both references.

var 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'));
 //At this time, there is still a global #button reference
 // elements dictionary.  The button element is still in memory and cannot be recycled by GC.
 }

Although we removed button with removeChild, we still keep a reference to #button in the elements object, in other words, DOM elements are still in memory.

Four, memory leak identification method

The new version of chrome is viewed in performance:


Steps:

  • Open developer tool Performance
  • Check Screenshots and memory.
  • The dot in the upper left corner starts recording.
  • Stop recording

The corresponding part of Heap in the figure can see the periodic decline of memory and the cycle of garbage collection. If the minimum value (we call it MIN) after garbage collection and MIN is continuously rising, then there must be a serious memory leak problem.

Some ways to avoid memory leaks:

  • Reduce unnecessary global variables or objects with long life cycle, and recycle useless data in time
  • Pay attention to the program logic and avoid “dead cycle”
  • Avoid creating too many objects

To sum up, there is one rule to follow.Principle: Return the unused items in time

V. Optimization of Use Scenarios for Garbage Collection

1. array optimization

Assigning [] to an array object is a shortcut to empty the array (for example: arr = []; However, it should be noted that this method creates a new empty object and turns the original array object into a small piece of memory garbage! In fact, assigning the array length to 0(arr.length = 0) can also achieve the goal of emptying the array, and at the same time can realize the reuse of the array and reduce the generation of memory garbage.

const arr = [1, 2, 3, 4];
 Log ('sailing on the waves');
 Arr.length = 0 // The number can be cleared directly without changing the array type.
 // arr = [];  Although the A variable is made into an empty array, an empty array object is reapplied on the heap.

2. Reuse objects as much as possible

Objects should be reused as much as possible, especially when new objects are created in places such as loops. Unused objects should be set to null as far as possible and be garbage collected as soon as possible.

Var t = {} // each cycle creates a new object.
 for (var i = 0;   i < 10;  i++) {
 // var t = {};  //Every cycle creates a new object.
 t.age = 19
 t.name = '123'
 t.index = i
 console.log(t)
 }
 T = null // If the object is no longer used, it is immediately set to NULL;  Waiting for garbage collection.

3. The function expression in the loop, which can be reused, is best placed outside the loop.

//It is better not to use function expressions in loops.
 for (var k = 0;   k < 10;  k++) {
 var t = function(a) {
 //10 times function object was created.
 console.log(a)
 }
 t(k)
 }
//Recommended usage
 function t(a) {
 console.log(a)
 }
 for (var k = 0;   k < 10;  k++) {
 t(k)
 }
 t = null

To recommend a useful BUG monitoring toolFundebug, welcome to try free!

Welcome to pay attention to the public number:Front end craftsmanWe witness your growth together!
image

Reference material