20170605-内存走漏和渣滓接纳

渣滓接纳的必要性

由于字符串、对象和数组没有牢固大小,所以当他们的大小已知时,才能对他们举行动态的存储分派。JavaScript顺序每次建立字符串、数组或对象时,诠释器都必须分派内存来存储谁人实体。只需像如许动态地分派了内存,终究都要开释这些内存以便他们能够被再用,不然,JavaScript的诠释器将会斲丧完体系中所有可用的内存,形成体系崩溃。 —《JavaScript威望指南(第四版)》

JavaScript的诠释器能够检测到什么时刻顺序不再运用一个对象了,当他肯定了一个对象是无用的时刻,他就晓得不再须要这个对象,能够把它所占用的内存开释掉了。比方:

var a = "before";
var b = "override a";
var a = b; //重写a

这段代码运转以后,“before”这个字符串失去了援用(之前是被a援用),体系检测到这个现实以后,就会开释该字符串的存储空间以便这些空间能够被再利用。

渣滓接纳道理

最常采纳的渣滓接纳有两种要领:标记消灭、援用计数

标记消灭

当实行流入到一个函数中时,会建立该函数的实行环境,实行环境中的变量都会被标记为“进入环境”,从逻辑上讲,永久不能开释“进入实行环境”变量所占用的内存。由于只需实行流入响应的实行环境,就可能会用到这些变量。
渣滓收集器在运转的时刻会给存储在内存的中的变量都加上标记。然后,它会去掉环境中的变量以及被环境中的变量援用的变量的标记。而在此以后再被加上标记的变量将被视为预备删除的变量。末了,渣滓收集器完成内存消灭事情,烧毁那些带标记的值并接纳它们所占用的内存空间。

援用计数

另一种不太罕见的渣滓接纳战略是援用计数。援用计数的寄义是跟踪纪录每一个值被援用的次数。当声清楚明了一个变量并将一个援用范例赋值给该变量时,则这个值的援用次数就是1。相反,假如包括对这个值援用的变量又取得了别的一个值,则这个值的援用次数就减1。当这个援用次数变成0时,则申明没有办法再接见这个值了,因而就能够将其所占的内存空间给收回来。如许,渣滓收集器下次再运转时,它就会开释那些援用次数为0的值所占的内存。

然则用这类要领存在题目:

function problem(){
    var objA = new Object()
    var objB = new Object()
    objA.someOtherObject = objB
    objB.someOtherObject = objA
}

在这个例子中,objA和objB经由过程各自的属性互相援用;也就是说这两个对象的援用次数都是2。在采纳援用计数的战略中,由于函数实行以后,这两个对象都离开了作用域,函数实行完成以后,objA和objB还将会继承存在,由于他们的援用次数永久不会是0。如许的互相援用假如说很大批的存在就会致使大批的内存走漏。

引发内存走漏的操纵

用全局变量缓存数据

将全局变量作为缓存数据的一种体式格局,将以后要用到的数据都挂载到全局变量上,用完以后也不手动开释内存(由于全局变量援用的对象,渣滓接纳机制不会自动接纳),全局变量逐步就积累了一些不必的对象,致使内存走漏

var x = [];
function grow() {
  x.push(new Array(1000000).join('x'));
  /*
     运用x数组举行某些操纵
  */
  setTimeout(grow, 1000);
}
grow()

没有清算的DOM元素援用

(function () {
  var nodes = '';
  var item = {
    // 为了凸显
    name: new Array(1000000).join('x')
  }
  nodes = document.getElementById("nodes")
  nodes.item = item
  nodes.parentElement.removeChild(nodes)
})()

notes变量指向的是页面中的一个元素(也是内存中的一块空间),当将 notes 对应的元素从页面中移除后,其在内存中对应的空间由于依然由notes变量指向(援用),因而渣滓接纳机制不会将这块内存空间接纳,从而致使内存走漏

 被忘记的定时器或者回调

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

如许的代码很罕见, 假如id为Node的元素从DOM中移除, 该定时器仍会存在, 同时, 由于回调函数中包括对someResource的援用, 定时器表面的someResource也不会被开释.

闭包轮回援用

var theThing = null  
var replaceThing = function () {
  var originalThing = theThing
  var unused = function () {
    if (originalThing)
      console.log("hi")
  }
  theThing = {
    longStr: new Array(1000000).join('*'),
    someMethod: function () {
      console.log(someMessage)
    }
  };
};
setInterval(replaceThing, 1000)

这类内存走漏的剖析请参考here

参考

《JavaScript威望指南》
javascript典范内存走漏及chrome的排查要领

    原文作者:jhhfft
    原文地址: https://segmentfault.com/a/1190000009658315
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞