首页 关于我们

js的垃圾回收机制

Word count: 1.1k / Reading time: 3 min
2015/11/06 Share

简介

js无法管理内存单元,内存的分配和回收都是自动进行的。但是自动并不意味着可以不用管。如果自动分配的内存,因为使用问题而无法释放,这就会造成内存泄露了。要了解js的垃圾回收,先得清楚内存的生命周期。不管什么语言,内存生命周期基本一致:分配所需空间 –> 读写分配的内存空间 –> 不需要的时候将其释放\归还 。那垃圾回收则是跟第三步相关。那回收的关键就落在如何判断“不需要”了。全局变量不会被回收,局部变量才可以回收。

要想完全将无用的内存释放掉,显然是无法通过某种算法做到的,垃圾回收只能近似地把无用的内存回收。而这种近似地算法有:引用计数和标记-清除。

引用

垃圾回收算法主要依赖于引用的概念。js地数据类型分为基本类型和引用类型,引用类型就是为了解决复杂数据结构的数据不要存储多份的问题。在内存管理的环境中,一个对象如果有访问另一个对象的权限(隐式或者显式),叫做一个对象引用另一个对象。例如,一个Javascript对象具有对它 原型 的引用(隐式引用)和对它属性的引用(显式引用)。

引用计数法

给每个分配的变量一个count记录引用的次数,当函数结束的时候,会扫描一下这个计算,如果变量的引用次数为零,则表明该变量没有引用指向它,则其可被回收。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var o = { 
a: {
b:2
}
};
// 两个对象被创建,一个作为另一个的属性被引用,另一个被分配给变量o
// 很显然,没有一个可以被垃圾收集
var o2 = o; // o2变量是第二个对“这个对象”的引用

o = 1; // 现在,“这个对象”的原始引用o被o2替换了
var oa = o2.a; // 引用“这个对象”的a属性
// 现在,“这个对象”有两个引用了,一个是o2,一个是oa

o2 = "yo"; // 最初的对象现在已经是零引用了
// 他可以被垃圾回收了
// 然而它的属性a的对象还在被oa引用,所以还不能回收
oa = null; // a属性的那个对象现在也是零引用了
// 它可以被垃圾回收了

这个算法的不足是:如果一个对象引用另一个(形成了循环引用),他们可能“不再需要”了,但是他们不会被回收。
所以才有了标记-清除算法。

标记-清除算法

在标记清除算法里改变了策略,不以引用为0作为标识,而是以是否可达,即是否可以有跟对象访问得到。即使存在循环引用,但是由根不可达的时候,也将其回收。这个算法中有两个角色:

  • collector:垃圾收集器
  • mutator:内存使用者,可以分配、读取、写入内存

collector可以回收不再使用的内存来供mutator进行NEW操作的使用。

其有两个阶段:标记(mark)和清除(sweep),

  • 标记阶段:collector从mutator根对象开始进行遍历,对从mutator根对象可以访问到的对象都打上一个标识,一般是在对象的header中,将其记录为可达对象。

  • 清除阶段:collector对堆内存(heap memory)从头到尾进行线性的遍历,如果发现某个对象没有标记为可达对象-通过读取对象的header信息,则就将其回收。

垃圾回收机制的时机

垃圾回收机制是由程序自动执行的,其会根据需要来执行回收程序。delete,并不会触发垃圾回收,与直接释放内存(只能通过解除引用来间接释放)没有关系。

(Node与其他语言不同的一个地方,就是其限制了JavaScript所能使用的内存(64位为1.4GB,32位为0.7GB))

CATALOG
  1. 1. 简介
  2. 2. 引用
  3. 3. 引用计数法
  4. 4. 标记-清除算法
  5. 5. 垃圾回收机制的时机