1. 垃圾回收机制概述
垃圾回收,即是指在编程语言中,对于不再使用的内存进行清理的过程。程序员在使用JavaScript编程语言时,不需要调用垃圾回收方法来进行内存的释放,而是由JavaScript引擎负责管理内存的回收,该过程由垃圾回收器自动进行,从而减轻了程序员对内存管理的负担。
JavaScript的垃圾回收机制采用的是自动垃圾回收算法,这是一种自动管理内存的算法,其工作原理是在JavaScirpt运行程序的时候,周期性地监视内存堆之中所有对象的状态,然后进行垃圾的回收。
1.1. JavaScript垃圾回收器分类
在JavaScript中,具有两种垃圾回收器。
V8引擎中的垃圾回收器。 V8 引擎采用了两种垃圾回收策略 - 分代回收策略和标记清除策略。
引用计数式的垃圾回收器。 这一种垃圾回收器已经被弃用,仅是提一下。
1.2. JavaScript垃圾回收算法
JavaScript中采用的垃圾回收算法主要分为以下两种:
标记清理算法:该算法描述了在JavaScript在运行程序时,监视所有对象是否被引用,被引用的对象不会被回收;而那些未被引用的对象则被视为垃圾,即可被清除回收。
分代回收算法:当一个对象在生成的时候,一般会被分为新生代和老生代两类对象。对于新生代对象,JavaScript采用的是Scavenge算法,该算法建立自己的内存空间,对于对象的生命周期进行一个短期的跟踪,随后对此清除垃圾对象并将剩下的对象转移到老生代空间里;而对于老生代对象,则会采用标记清理算法进行垃圾的回收。
2. JavaScript垃圾回收机制详解
2.1. 标记清理算法
在JavaScript的垃圾回收中,其实就是用标记清除算法来进行的。标记清除算法的核心思路就是标记出所有正在使用的对象,清除掉其他未被标记的垃圾对象。当程序空闲的时候,进行垃圾回收。
JavaScript 的垃圾回收器,是把所有的内存空间,分为两个区域 --> 已使用和未使用区域。当内存已经使用完了,即已使用空间不够,JavaScript 就会启动垃圾回收的算法,把已使用中的垃圾对象都释放掉,从而腾出空间来。
function theGarbageCollection() {
var a = { b:1 }; // Reference Counting -> 1
var b = a; // Reference Counting -> 2
a = null; // Reference Counting -> 1 :: Marked for Deletion
var c = { d: 2 }; // Reference Counting -> 1
var d = c; // Reference Counting -> 2
var e = c; // Reference Counting -> 3
c = null; // Reference Counting -> 2
d = null; // Reference Counting -> 1
e = null; // Reference Counting -> 0 :: Garbage Collected
}
theGarbageCollection(); // Memory Cleaned
2.2. 分代回收算法
JavaScript 通过将堆内存划分为两个部分 -- 新生代存储区和老生代存储区 -- 进行多代垃圾回收。新生代内存空间为几MB,存储的都是短命的变量和对象。新生代存储的是处在内存生命周期的起始时期。由于典型的 JavaScript 程序会有大量的对象被创建出来执行短时间,然后就不再使用,所以当垃圾回收机制开始退去某一部分内存时,就不会涉及太多的对象。
内存生命周期短的叫做新生代对象,经过一段时间的生命周期变长之后,就变成了老生代对象。对于那些经过多次垃圾回收,但依然还是存在的对象,则被认为是存活较长时间的对象,就会存储到老生代存储区里面。
3. JavaScript垃圾回收器
3.1. V8引擎中的垃圾回收器
V8 引擎中,有两种主流的垃圾回收策略,分别是分代回收策略和标记清除策略。
3.1.1. 分代回收策略
分代回收策略是基于以下假设:新出来的对象很快就会变成垃圾对象,而存活时间长一点的对象很有可能会存在较长的时间。
新生代存储空间较小,而老生代存储空间较大。新生代存储空间的垃圾回收方案采用的是Scavenge算法,Scavenge算法会在堆内存中找一块未被使用的内存空间,并将其分为两部分,分别存储新生代和老生代对象。同时,新生代对象一般也不太会占用过多的内存空间。
var v8InGB = function(v8) {
return ((v8 / 1024 / 1024) / 1024).toFixed(2) + 'GB';
};
console.log(v8InGB(v8.getHeapStatistics().heap_size_limit)); // 1.39GB
3.1.2. 标记清除策略
标记清除策略采用的是采用的是移动复制策略,并会在内存空间不足时,首先遍历JavaScript堆内存里面的对象,找出活着的对象并把其复制到一块保存所有新生代活着的对象的内存空间里,接着会对已经复制的对象进行紧缩整理,可以在内存空间内创造更大的绰绰有余的空间。
3.2. 引用计数式的垃圾回收器
引用计数式的垃圾回收器是一种已经被弃用的回收机制,它监测的是对象的引用次数。当一个对象被使用,则被加入到活跃的对象链表里;否则被被存储到不活跃的对象链表中。当不活跃链表中包含的对象引用计数为0时,则被回收。
4. 总结
JavaScript垃圾回收器采用的是自动垃圾回收算法,该算法可有效减轻程序员对内存管理的负担。JavaScript采用的是标记清除算法,并在堆内存中将对象分成新生代对象和老生代对象,然后分别采用Scavenge算法和标记清除算法进行垃圾的回收。
值得注意的是,Scavenge算法在短时间内进行一次内存回收,当程序执行中堆积了大量的对象时,需要进行“动态增长”,否则过分小的堆内存空间就会导致垃圾回收的频率过高,从而影响JavaScript程序的执行效率。