内存泄露
不再用到的内存,没有及时释放,就叫做内存泄漏。
对于持续运行的服务进程(daemon),必须及时释放不再用到的内存。否则,内存占用越来越高,轻则影响系统性能,重则导致进程崩溃

JavaScript 的垃圾收集机制
javascript具有自动垃圾收集机制,也就是说,执行环境会负责管理代码执行过程中的使用的内存。
JavaScript中最常用的垃圾收集方式是标记清除(mark-and-sweep)。当变量进入环境(例如,在函数中声明一个变量)时,就将这个变量标记为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占的内存,因为只要执行流进入相应的环境,就可能用到它们。而当变量离开环境时,这将其 标记为“离开环境”。

常见内存泄漏的原因
虽然JavaScript 会自动垃圾收集,但是如果我们的代码写法不当,会让变量一直处于“进入环境”的状态,无法被回收。下面列一下内存泄露常见的几种情况。

全局变量引起的内存泄漏

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

实际上是:

function foo(arg) {
    window.bar = "this is an explicit global variable";
}

如果bar是仅在foo函数作用域内承载引用,并且你忘记用var来声明的变量,一个意外的全局变量就被创建了。在这个例子中,泄漏一个单一字符串不会有太大害处,但这的确是不好的。

另一种意外全局变量被创建的方式是通过this

function foo() {
    this.variable = "potential accidental global";
}
foo();

为了阻止这种错误发生,在你的Javascript文件最前面添加'use strict;'。这开启了解析JavaScript的阻止意外全局的更严格的模式

全局变量注意事项

尽管我们讨论了一些意外的全局变量,但是仍有一些明确的全局变量产生的垃圾。它们被定义为不可回收(除非定义为空或重新分配)。尤其当全局变量用于 临时存储和处理大量信息时,需要多加小心。如果必须使用全局变量存储大量数据时,确保用完以后把它设置为 null 或者重新定义。与全局变量相关的增加内存消耗的一个主因是缓存。缓存数据是为了重用,缓存必须有一个大小上限才有用。高内存消耗导致缓存突破上限,因为缓 存内容无法被回收。

闭包引起的内存泄漏

function a(){
  var i=0;
  function b(){
    alert(++i); // 被闭包所引用,不会被回收
  }
  return b;
}
var c=a();//执行完成以后a函数里面的i变量不能被释放,因为有闭包的维持。当函数a的内部函数b被函数a外的一个变量引用的时候,就创建了一个闭包。
c();

基础的DOM泄漏

当原有的DOM被移除时,子结点引用没有被移除则无法回收。

var select = document.querySelector;
var treeRef = select('#tree');

var leafRef = select('#leaf');   //在COM树中leafRef是treeFre的一个子结点

select('body').removeChild(treeRef);//#tree不能被回收入,因为treeRef还在

解决方法:

treeRef = null;//tree还不能被回收,因为叶子结果leafRef还在
leafRef = null;//现在#tree可以被释放了

dom清空或删除时,事件未清除导致的内存泄漏

<div id="container">  
</div>

$('#container').bind('click', function(){
    console.log('click');
}).remove();

解决方法:

$('#container').bind('click', function(){
    console.log('click');
}).off('click').remove();
//把事件清除了,即可从内存中移除  

timer定时器泄漏

var val = 0;
for (var i = 0; i < 90000; i++) {
  var buggyObject = {
    callAgain: function() {
      var ref = this;
      val = setTimeout(function() {
        ref.callAgain();
      }, 90000);
  }
}
buggyObject.callAgain();

这个时候你无法回收buggyObject

//虽然你想回收但是timer还在
buggyObject = null;

解决办法,先停止timer然后再回收

//解决方法,先停止定时器
clearTimeout(val);
buggyObject = null;

bigtooth
26 声望2 粉丝