垃圾回收中的引用计数与标记清除

1,191 阅读2分钟

引用计数和标记清除都属于垃圾回收机制的策略,用于自动内存管理。

引用计数

引用计数的原理是通过判断对象的被引用次数来决定是否回收其内存。

具体过程是当初始化一个对象时会将其引用计数标记为1,当其被引用时计数+1,取消引用时则计数-1,当计数为0时该对象就会进入待清除状态,等待后续内存回收。

引用计数会导致一个问题,那就是“无法回收循环引用对象的内存,会造成内存泄漏”。即使我们将对象设置为null,由于循环引用导致它的计数永远不为0,因此内存不会被回收,造成内存泄漏。

以下是一个例子:

// 创建两个对象
let obj1 = { name: 'obj1' };
let obj2 = { name: 'obj2' };

// 互相引用
obj1.child = obj2;
obj2.parent = obj1;

// 引用计数器增加
// obj1: 2, obj2: 2

// 解除部分引用
delete obj1.child;
// obj1: 2, obj2: 1

// 解除所有引用
obj1 = null;
obj2 = null;
// 对象并没有被回收

标记清除

标记清除的原理是通过分析对象的"可达性"来决定来是否回收其内存的。

具体的做法是从根对象出发,开始遍历程序的整个"对象图谱"(垃圾回收器用来遍历和标记的数据结构),使用“三色算法”:

  1. 所有对象初始化为白色,将第一次遍历到的对象标记为灰色,加入到标记栈中,然后遍历其子对象。
  2. 当遍历到灰色的对象时,说明存在循环引用,但是该对象仍然可达,不会被清除。
  3. 当遍历完对象的所有子对象,将该对象标记为黑色并弹出标记栈。

利用标记清除,当循环引用的对象被置为null时,由于它们不可达,所以即使它们存在循环引用也会被回收,当然前提是循环引用的对象被置为null,满足不可达条件。

待补充...