1.管理内存

js的内存分配

在编写JavaScript程序时,开发人员不再关心内存使用问题,所需内存的分配以及无用内存的回收完全实现了自动管理。

内存声明周期:
  1. 分配你所需要的内存
  2. 使用分配到的内存(读、写)
  3. 不需要时将其释放\归还

由于字符串、对象和数组没有固定大小,所有当他们的大小已知时,才能对他们进行动态的存储分配。JavaScript程序每次创建字符串、数组或对象时,解释器都必须分配内存来存储那个实体。

只要像这样动态地分配了内存,最终都要释放这些内存以便他们能够被再用,否则,JavaScript的解释器将会消耗完系统中所有可用的内存,造成系统崩溃。

2. 垃圾回收机制概念

MDN中给出垃圾回收的定义:

JavaScript是在创建变量(对象,字符串等)时自动进行了分配内存,并且在不使用它们时“自动”释放,释放的过程称为垃圾回收。

Javascript具有自动垃圾回收机制(GC:Garbage Collecation),也就是说,执行环境会负责管理代码执行过程中使用的内存。

其原理是:垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存。但是这个过程不是实时的,因为其开销比较大并且GC时停止响应其他操作。

垃圾回收算法主要依赖于引用的概念,在内存管理的环境中,一个对象如果有访问另一个对象的权限(隐式或者显式),叫做一个对象引用另一个对象。

垃圾回收最常用的就是标记清除和引用计数。

2.1 标记清除

  从2012年起,所有现代浏览器都使用了标记-清除垃圾回收算法。所有对JavaScript垃圾回收算法的改进都是基于标记-清除算法的改进,并没有改进标记-清除算法本身和它对“对象是否不再需要”的简化定义。

  当变量进入执行环境时,就标记这个变量为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到他们。当变量离开环境时,则将其标记为“离开环境”。

  垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记。然后,它会去掉环境中的变量以及被环境中的变量引用的标记。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。

  最后,垃圾收集器完成内存清除工作,销毁那些带标记的值,并回收他们所占用的内存空间。标记清除是JavaScript最重要的垃圾收集方式,目前为止,IE9+、Firefox、Opera、Chrome、Safari的js实现使用的都是标记清除的垃圾回收策略或类似的策略,只不过垃圾收集的时间间隔互不相同。

如下是一个释放内存的示例:

function addTen(num){  
var sum += num;  //垃圾收集已将这个变量标记为“进入环境”。
return sum;      //垃圾收集已将这个变量标记为“离开环境”。
}
addTen(10);  //输出20复制代码

当运行addTen这个函数时,将这个变量标记为“进入环境”,从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其标记为“离开环境”。

垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记,然后,他会去掉环境中的变量以及被环境中的变量引用的变量的标记。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后,垃圾收集器完成内存清除工作,销毁那些带标记的值并回收它们所占用的内存空间。

2.2 引用计数

引用计数是最初级的垃圾收集算法,不太常用。引用计数的含义是跟踪记录每个值被引用的次数,当声明了一个变量并将一个引用类型赋值给该变量时,则这个值的引用次数就是1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数就减1。

当这个引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其所占的内存空间给收回来。这样,垃圾收集器下次再运行时,它就会释放那些引用次数为0的值所占的内存。

该方法会导致循环相互引用得不到释放,如果大量的存在,就会导致内存泄露;

function f(){
  var o = {};
  var o2 = {};
  o.a = o2; // o 引用 o2
  o2.a = o; // o2 引用 o

  return "azerty";
}

f();复制代码

如上代码中,创建了两个对象o,o2,并且o和o2相互引用,形成了一个循环。他们被调用之后会离开函数作用域,可以被回收,但是引用计数算法考虑到它们互相都至少有一次引用,所以不会被回收。

另一方面,早期的IE浏览器中,COM对象的垃圾收集机制采用的就是引用计数策略,只有在IE中涉及COM对象,就会存在循环引用的问题。为了避免这样的循环引用,最好在不使用他们的时候手工断开即设置为null值,断开原生JavaScript对象与原生DOM元素之间的连接。

3.性能问题

垃圾收集器是周期性运行的,而且如果为变量分配的内存数量很可观,那么回收工作量也是相当大的。在这种情况下,确定垃圾收集的时间间隔是一个非常重要的问题。

随着IE7的发布,触发垃圾收集的变量分配、字面量和数组元素的临界值被调整为动态修正。这一简单的调整,极大地提升了在运行包含大量JavaScript页面时的性能。

4.总结

JavaScript变量可以用来保存两种类型的值:基本类型和引用类型,这两种值具有以下特点:

离开作用域的值将被自动标记为可以回收,在垃圾收集期间被删除;“标记清除”是目前主流的垃圾收集算法,这种算法的思想是给当前不使用的值加上标记,然后再回收其内存; 当代码中存在循环引用现象时,“引用计数”算法就会导致问题; 解除变量的引用不仅有助于消除循环引用现象,而且对垃圾收集也有好处。 为了确保有效的回收内存,应该及时解除不再使用的全局变量、全局对象属性以及循环引用变量的引用。