谈一谈浏览器垃圾回收机制

JS会在创建变量时自动分配内存,在不使用时会自动周期性的释放内存,释放的过程就叫“垃圾回收”。

一方面自动分配内存减轻了开发者的负担,开发者不用过多的去关注内存的使用,但是一方面,正是因为自动回收,所以不清楚回收机制,很容易造成混乱,而混乱就容易造成“内存泄漏”。
由于是自动回收,所以就存在一个“内存是否需要被回收”的问题,但是这个问题的判定在程序中意味着无法通过某个算法去准确完整的解决。

回收算法

垃圾回收对是否需要回收的问题主要依赖对于变量的判定是否可访问,由此衍生出两种主要的回收算法:

  • 标记清理
  • 引用计数

标记清理

标记清理是JS最常用的回收策略,2012年后所有浏览器都使用了这种策略,此后对于回收策略的改进也是基于这个策略进行改进的。

其策略是:

  • 变量进入上下文(也可以理解为作用域),会加上标记,证明其存在于该上下文【逻辑上讲,永远不应该释放它们的内存,因为只要上下文中的代码在运行,就有可能用到它们】flag = 存活
  • 变量使用完成之后,将非活跃的变量标记为准备删除的变量,等待垃圾回收器回收flag = 可回收
  • 执行内存清理,销毁带可回收标记的变量并回收之前被占用的内存flag = 未使用

局限:

  • 由于是从根对象(全局对象)开始查找,对于那些无法从根对象查询到的对象都将被清除;
  • 回收后会形成内存碎片,影响后面申请大的连续内存空间

引用计数

引用计数策略相对而言不常用,因为弊端较多。其思路是对每个值记录它被引用的次数,通过最后对次数的判断(引用数为0)来决定是否保留。
具体规则:

  • 声明一个变量,赋予它一个引用值时,计数+1;
  • 同一个值被赋予另外一个变量时,引用+1;
  • 当引用该值的变量被其他值覆盖时,引用-1;
  • 引用为0时,回收内存;

局限:
最重要的问题就是 相互引用问题

1
2
3
4
5
6
function refProblem(){
let a = new Object();
let b = new Object();
a.c = b;
b.c = a; //相互引用
}

根据之前提到的规则,两个都相互引用了,引用计数不为0,所以两个变量都无法回收。如果频繁的调用该函数,则会造成严重的内存泄漏。

文章作者: qinwei
文章链接: https://qw-null.github.io/2022/09/05/谈一谈浏览器垃圾回收机制/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 QW's Blog