当前位置:网站首页>Common memory leakage scenarios in JS

Common memory leakage scenarios in JS

2020-11-08 16:13:00 WindrunnerMax

Common memory leak scenarios

Memory leak Memory Leak It refers to the heap memory that has been dynamically allocated in the program. Due to negligence or error, the program is not released or cannot be released , Cause the waste of system memory , Causes the procedure to run the speed to slow down even the system crashes and so on the serious consequence . Memory leaks are not physical disappearances , But after the application allocates some memory , Because of a design mistake , As a result, the control of this segment of memory is lost before releasing it , This results in a waste of memory . For memory leak detection ,Chrome Provides performance analysis tools Performance, Can be more convenient to view the occupation of memory and so on .

Memory recovery mechanism

image C Language such as the underlying language generally has the underlying memory management interface , for example malloc() and free() etc. , about JavaScript When a variable is created, it automatically allocates memory , And automatically release them when they're not in use . stay Js Reference types of the seven basic types Object Variable, which occupies a large memory space and the size is not fixed , The object is actually stored in heap memory , Storing pointers to objects in stack memory , Access to objects is by reference . Variables executed in the stack area are accessed by values , When its scope is destroyed, the variable is destroyed , And heap variables that use reference access , After a scope disappears, there may still be references in the outer scope or other scopes , It can't be destroyed directly , At this point, we need to calculate whether the heap variable belongs to the variable that is no longer needed by the algorithm , To determine whether memory recycling is needed , stay Js There are two kinds of garbage collection algorithms: reference counting and mark clearing .

Reference counting algorithm

For the reference count garbage collection algorithm , Whether an object no longer needs to be simplified is defined as whether the object has other variables or objects referring to it , If there is no reference to the object , The object will be recycled by the garbage collection mechanism . ad locum , The concept of an object does not refer specifically to JavaScript object , It also includes function scope or global lexical scope . Reference count garbage collection algorithm is less used , Mainly in the IE6 And IE7 Wait for the lower version IE The browser uses .

var obj = {
    a : {
        b: 11
    }
}
//  At this point, two objects are created , One as the other a Properties are referred to as objects 1, The other was obj Variable references are called objects 2
//  In this case, both objects have referenced variables , Can't reclaim memory 

var obj2 = obj;
//  At this time, for obj Referenced objects 2, There has been a obj And Obj2 References to two variables 

obj = null;
//  take obj For the object 2 The reference of , At this point the object 2 There is still obj2 A quote 

var a2 = obj2.a;
//  Reference object 1, At this point the object 1 Yes a And a2 Two quotes 

obj2 = null;
//  Release object 2 A reference to , At this point the object 2 The number of citations for is 0, It can be recycled by garbage 
//  object 2 Of a Attribute references are released , At this point the object 1 Only a2 A quote 

a2 = null;
//  relieve a2 For the object 1 References to , At this point the object 1 It can be recycled by garbage 

But there is a limitation to the reference count garbage collection algorithm , When an object is referred to repeatedly , Will cause memory leaks , In other words, the reference count garbage collection algorithm cannot handle objects that are referred to in a circular way .

function funct() {
    var obj = {}; //  Name it object 1, In this case, the number of references is 1
    var obj2 = {}; //  Name it object 2, In this case, the number of references is 1
    obj.a = obj2; // obj Of a Property reference obj2, At this point the object 2 The number of citations for is 2
    obj2.a = obj; // obj2 Of a Property reference obj, At this point the object 1 The number of citations for is 2
    return 1;
    //  At this point, the stack is executed obj Variables and obj2 Variables are destroyed , object 1 With the object 2 Reduce the number of citations of 1
    //  object 1 The number of citations for is 1, object 2 The number of citations for is 1, Neither object will be garbage collected by the reference counting algorithm 
}

funct();
//  Two objects are created , And quote each other , It forms a cycle , They leave the function scope when called , So they're no longer needed , Can be recycled , However, they all reference each other at least once , So they won't be recycled .

Tag clearing algorithm

For the reference count garbage collection algorithm , Whether the object no longer needs simplification is defined as whether the object can be obtained , The algorithm sets up a root root The object of , stay Javascript Reagan is the global object , The garbage collector will periodically start at the root , Find all objects referenced from the root , Then look for the objects that these objects refer to , So keep looking down . Start at the root , The garbage collector will find all available objects and collect all unavailable objects , This solves the problem of circular references . All modern browsers use mark clean garbage collection algorithms , All right JavaScript The improvement of garbage collection algorithm is based on the improvement of mark clearing algorithm .

  • When the garbage collector runs, it marks all variables stored in memory .
  • then , It will remove the variables in the runtime environment and the variables referenced by the variables in the environment .
  • thereafter , Variables that are still marked are considered variables ready to be deleted , The reason is that these variables are no longer accessible in the runtime environment .
  • Last , The garbage collector completes the memory cleanup , Destroy the tagged values and reclaim the memory space they occupy .

Common memory leak scenarios

Unexpected global variables

stay JavaScript There is no strict definition of how to handle undeclared variables in , Global variables can be defined even in the scope of local functions , This unexpected global variable can store a lot of data , And because it can pass through global objects such as window Access to , Therefore, memory recycling does not consider it to be the memory that needs to be recycled, but always exists , It can only be released when the window is closed or the page is refreshed , Causing an unexpected memory leak , stay JavaScript In strict mode, this unexpected way of defining global variables throws an exception , You can also use eslint Perform a pre check of this state . In fact, it's not a good habit to define global variables , If you have to use global variables to store large amounts of data , Make sure you set it to zero when you're done null Or redefine it , One of the main causes of increased memory consumption associated with global variables is caching , Caching data is for reuse , The cache must have a size cap to be useful , High memory consumption causes the cache to breach the upper limit , Because the cached content cannot be recycled .

function funct(){
    name = "name";
}
funct();
console.log(window.name); // name
delete window.name; //  If you do not delete it manually, it will always exist without closing or refreshing the window 

Forgotten timer

timer setInterval It must be cleaned up in time , Otherwise, it may not be recycled because the variables or functions referenced in it are considered necessary , If internally referenced variables store a lot of data , May cause the page to occupy too much memory , This causes an unexpected memory leak .

<template>
  <div></div>
</template>

<script>
export default {
  creates: function() {
    this.refreshInterval = setInterval(() => this.refresh(), 2000);
  },
  beforeDestroy: function() {
    clearInterval(this.refreshInterval);
  },
  methods: {
    refresh: function() {
      // do something
    },
  },
}
</script>

Out of the DOM References to

Sometimes save DOM The data structure inside the node is very useful , For example, you need to quickly update a few lines of the table , Put each line DOM It makes sense to save it as a dictionary or an array . At the same time DOM The element has two references : In a DOM In the tree , The other one is in the dictionary . In the future, if you decide to delete these lines , You need to clear both references . Besides, consider DOM Reference problems within the tree or child nodes , If your JavaScript A table is saved in the code <td> References to , When you decide to delete the entire table in the future , Intuition holds that GC It will recycle everything except what was saved <td> Other nodes than , This is not the case , this <td> Are child nodes of the table , Child elements are referenced to parent elements , Because the code is preserved <td> References to , Causes the entire table to remain in memory , So it's saving DOM When the element is referenced , Be careful .

var elements = {
    button: document.getElementById("button"),
    image: document.getElementById("image"),
    text: document.getElementById("text")
};
function doStuff() {
    elements.image.src = "https://www.example.com/1.jpg";
    elements.button.click();
    console.log(elements.text.innerHTML);
    //  More logic 
}
function removeButton() {
    //  Button is  body  The offspring element of 
    document.body.removeChild(elements.button);
    elements.button = null; //  Clear the reference to this object 
}

Closure

Closure is JavaScript A key aspect of development , Closures allow you to access external function scopes from internal functions , In a nutshell, it can be said that a function scope can be accessed from another function scope instead of having to implement a scope chain structure in the function scope . Because a closure carries the scope of the function that contains it , So it takes up more memory than other functions , Overuse of closures can lead to excessive memory consumption , The closure that is no longer needed needs to be cleared manually after use .

function debounce(wait, funct, ...args){ //  Anti shake function 
    var timer = null;
    return () => {
        clearTimeout(timer);
        timer = setTimeout(() => funct(...args), wait);
    }
}

window.onscroll = debounce(300, (a) => console.log(a), 1);

The forgotten listener pattern

When the listener mode is implemented and the related event handling function is mounted in the component , When the component is destroyed and it is not cleared actively , The variables or functions that are referenced are considered necessary and will not be recycled , If internally referenced variables store a lot of data , May cause the page to occupy too much memory , This causes an unexpected memory leak .

<template>
  <div ></div>
</template>

<script>
export default {
  created: function() {
    global.eventBus.on("test", this.doSomething);
  },
  beforeDestroy: function(){
      global.eventBus.off("test", this.doSomething);
  },
  methods: {
    doSomething: function() {
        // do something
    },
  },
}
</script>

Forgotten event listeners

When the event listener mounts the relevant event handler in the component , When the component is destroyed and it is not cleared actively , The variables or functions that are referenced are considered necessary and will not be recycled , If internally referenced variables store a lot of data , May cause the page to occupy too much memory , This causes an unexpected memory leak .

<template>
  <div></div>
</template>

<script>
export default {
  created: function() {
      window.addEventListener("resize", this.doSomething);
  },
  beforeDestroy: function(){
      window.removeEventListener("resize", this.doSomething);
  },
  methods: {
      doSomething: function() {
        // do something
      },
  },
}
</script>

Forgotten Map

When using Map When storing objects , It's like breaking away from DOM References to , If you do not actively clear the reference , It also causes memory not to be recycled automatically , In the case of keys as objects , May adopt WeakMap,WeakMap Objects are also used to hold key value pairs , It is weakly referenced to a key and must be an object , And the value can be any object or original value , And because it's a weak reference to an object , It doesn't interfere with Js The garbage collection of .

var elements = new Map();
elements.set("button", document.getElementById("button"));
function doStuff() {
    elements.get("button").click();
    //  More logic 
}
function removeButton() {
    //  Button is  body  The offspring element of 
    document.body.removeChild(elements.get("button"));
    elements.delete("button"); //  Clear the reference to this object 
}

Forgotten Set

When using Set When storing objects , It's like breaking away from DOM References to , If you do not actively clear the reference , It also causes memory not to be recycled automatically , If needed Set Reference object , May adopt WeakSet,WeakSet Object allows you to store unique values for weak references to objects ,WeakSet The values in the object are also not repeated , And only weak references to objects can be saved , And because it's a weak reference to an object , It doesn't interfere with Js The garbage collection of .

var elements = new Set();
var btn = document.getElementById("button");
elements.add(btn);
function doStuff() {
    btn.click();
    //  More logic 
}
function removeButton() {
    document.body.removeChild(btn); //  Button is  body  The offspring element of 
    elements.delete(btn); //  eliminate Set Reference to this object in 
    btn = null; //  Clear references 
}

A daily topic

https://github.com/WindrunnerMax/EveryDay

Reference resources

https://zhuanlan.zhihu.com/p/60538328
https://juejin.im/post/6844903928060985358
https://jinlong.github.io/2016/05/01/4-Types-of-Memory-Leaks-in-JavaScript-and-How-to-Get-Rid-Of-Them/

版权声明
本文为[WindrunnerMax]所创,转载请带上原文链接,感谢