垃圾回收机制(GC):Garbage Collection
参考文章
原理
定期找出不再用到的变量,然后释放其内存。
具体方法
1. 标记清除法
- 基本步骤
- 垃圾收集器在运行时会给内存中的所有变量都加上一个标记,假设内存中所有对象都是垃圾,全标记为0;
- 然后从各个根对象开始遍历,把不是垃圾的节点改成1;
- 清理所有标记为0的垃圾,销毁并回收它们所占用的内存空间;
- 最后,把所有内存中对象标记重新修改为0,等待下一轮垃圾回收。
- 优点
实现简单,打标记一共就两种情况。
- 不足
- 内存碎片化:清除之后,剩余的对象内存位置是不变的,也会导致剩余空闲内存空间不连续,出现内存碎片。
- 分配速度慢:假设我们新建对象分配内存时需要大小为 size,由于空闲内存是间断的、不连续的,则需要对空闲内存列表进行一次单向遍历找出大于等于 size 的块才能为其分配。
标记整理法
标记整理法是标记清除法的加强版,解决了内存碎片的问题。
标记结束后,标记整理法会将没被清理的对象向内存的一端移动,解决了剩余内存空间分配不连续的问题。
2. 引用计数法(少用)
引用计数法提出一个思路,将对象是否被需要转化为对象是否被引用。如果没有引用指向该对象,对象将被垃圾回收机制回收。基本思路是:跟踪记录每个值被使用的次数。
- 基本步骤
- 当声明了一个变量并且将一个引用类型赋值给该变量的时候这个值的引用次数就加 1;
- 如果该变量的值被其他的值覆盖了,则引用次数减 1;
- 当这个值的引用次数变为 0 时,说明没有变量在使用,这个值没法被访问了。垃圾回收器会清理掉引用次数为 0 的值的内存。
let o = new Object(); // new出来的这个对象的值(甲)被o引用,甲引用次数+1
let o1 = o; // 对象赋值是传址,所以o1也引用了甲,甲引用次数+2
o = null // 变量o赋值为null,切断了内存中的指向,甲引用次数-1
b = null // 同上,甲引用次数-1
// 甲引用次数已为0,垃圾回收器将甲清除
- 优点:
- 及时:引用计数在引用值为 0 时,它立即被回收;而标记清除法需要等一段时间再去清理。
- 无需遍历所有对象:引用计数法在引用时计数即可。
- 缺点:循环引用
/*
A指向对象甲,B指向对象乙,A.b指向对象乙,B.a指向对象甲。
所以甲和乙的引用次数都为2,因此无法清除。
*/
function demo(){
let A = new Object();
let B = new Object();
A.b = B;
B.a = A;
}
现在大部分还是采用第一种方法,另外 V8引擎也基于标记清除法做了优化,且看下篇文章。