乐趣区

关于javascript:聊聊Javascript-垃圾回收机制一

引子

我突然想起来了还有个博客账号,最近工作工作较少,所以间歇性踌躇满志又更新一下 -\_-

注释

背景介绍 - 什么是垃圾回收

垃圾回收 (garbage-collection) 是指对已调配的内存进行回收,当咱们在创立一些变量,函数,对象时,例如:

var a = "Hello";
var getName = function (){ // 函数体}

都须要分配内存,而当这些值不再被应用的时候,js 就须要在适合的时候将这部分的内存进行回收,这就是 垃圾回收机制,对于一些大型应用程序来说,垃圾回收能够无效进步性能。在 js 里,执行垃圾回收是主动执行的,不对外提供任何接口,不过还是有必要适当理解下它的原理。

回收条件 - 什么状况能够进行回收

理解完垃圾回收的概念当前,首要的问题就来了:对于已调配进来的内存,什么状况下能够进行垃圾回收呢?

分割下理论生存:咱们会把什么样的货色送去回收呢,那当然是 确定当前基本用不到的货色。

在 js 里,也是一样,对于再也无法访问到的值,咱们就要进行回收。

个别案例

举个简略例子:

var user = {name: 'Leo'}; // 第一步,创立一个对象并把内存地址赋值给 user
user = null;  // 第二步,批改 user 的内存地址

之前在其余文章曾经说过,创立援用类型值的时候,赋值给变量的本质上是内存地址。执行上述代码的第一步之后,能够通过 window.user 拜访到 {name: 'Leo'} 对象,


然而在执行 Leo = null 之后,这个变量对象其实曾经 无法访问到 了。也就达到了能够被回收的条件。

再看个略微简单的案例:

var user = {
  name: 'Leo',
  friend: {name: 'John'}
}; 
// 接下来移除援用
user.friend = null;// 第一种状况,批改 user.friend 的地址 
user = null;// 第二种状况

这个例子中比后面多减少了一个对象{name: 'John'}, 移除援用前后对应的图如下:

相互援用

当初大家应该对什么叫做 无奈被拜访 有一个大略的概念了。那么来个非凡一点的:

// '朋友圈' 函数 让传入的两个人成为敌人,并返回这个小圈子
function circleOfFriends(user1, user2){
   user1.friend = user2;
   user2.friend = user1;
   return {
     user1,
     user2
   }
} 
var circle = circleOfFriends({name: 'Leo'},{name: 'John'});

执行这个之后,关系图如下:

window能够通过 circle 拜访到 circle 对象,circle能够通过 user1`user2 属性拜访到{name: ‘Leo’},{name: ‘John’}` 两个对象,所以此时这三个对象都还是可拜访的。

如果接下来顺次执行以下步骤:

circle.user1 = null; // 此时如果要拜访 {name: 'Leo'} 对象 还能够通过 circle.user2.friend 来实现,所以它仍然是能够拜访到的

接着执行:

circle.user2.friend = null; // 此时 {name: 'Leo'} 被彻底孤立,再也不可拜访,它满足能够被回收的条件

留神,从图中能够看到 {name: 'Leo'} 还有援用其余对象,然而它本身是通过任何形式都无奈被拜访到的。所以此时它还是属于可回收的。

当然,如果咱们不执行下面的语句,而是间接执行:

circle = null;

此时,circle 指向的对象,{name: ‘Leo’}对象,{name: ‘John’}对象,都将变得不可拜访,即便他们之间外部是有相互援用关系。

到这里,咱们仿佛就能够演绎出,满足可被回收的条件:

从全局对象登程,只有不可能间接或者间接被拜访到的值,就满足可回收的条件;

真的仅仅是这样吗?咱们仿佛还脱漏了一个重要的中央 – 执行环境。曾几何时,咱们在介绍闭包的时候讲过 执行环境 作用域链 ,很显然的,以后执行环境和作用域链上的值 也属于 可拜访 的,例如最简略的函数例子:

  var b = 2;
  function getA(){
    var a = 1;
      return a ;
  }
  getA();

当运行到 getA() 之前时,变量 b 是可拜访的,然而变量 a 是不可拜访的, 当执行 getA 函数外部时,变量 a 就是能够拜访的了,当然此时变量 b 也能够拜访。
所以咱们须要欠缺一下下面的论断:

从以后执行环境以及作用域链上可拜访的对象登程,任何能够被拜访的对象,都算是可拜访对象。

当然 如果认为 执行环境 也是以后全局对象可拜访的对象之一,那一样能够用后面的简化版论断,表述形式没必要太较真,重点是要记住执行环境和作用域链这个要点。

(有印象的同学无妨思考下很久之前说过的闭包,思考下闭包和垃圾回收机制的关系,前面有机会也会持续补充阐明)

垃圾回收的算法

标记革除法

其实下面的内容讲完,标记革除法的思路,也就根本分明了。标记革除法,顾名思义分为 2 个步骤:

  1. 标记:从根节点(这里的根节点,指的就是上文提到的 全局对象以及以后执行环境以及作用域链上可拜访的对象)登程,深度优先遍历所有能够拜访到的节点,并打上 ” 可拜访 ” 的标记
  2. 革除:从所有节点里,革除掉没有打上标记的节点。

这里介绍的是外围思路,具体实际的时候有些中央是能够优化的,例如第一步标记的过程,对于某些节点有可能被反复遍历的,就像后面提到的 ’ 朋友圈 ’ 模型中,既能够通过 circle 拜访其中的某个对象,也能够通过某个 userfriend属性拜访到,那么为了防止反复遍历,咱们就能够另外用一种标记,来标识示意节点曾经被遍历过,具体算法会在后续的系列文章介绍。

援用计数法

其实 mdn 还介绍了晚期的另一种回收算法,咱们也介绍提一下。

援用计数法顾名思义是就是 先记下某个变量被援用的总次数,而后当被援用次数为 0 时,就示意可回收。

这个思路咋一看和标记革除法是很类似的,然而实际上有个很显著的区别,就是在后面 相互援用 的例子中:两个 user 其实都是不可取得的,然而因为相互援用,它们的被援用次数并不为 0,那么依照援用计数法,这两个对象就不会被革除,这是有问题的,所以从 2012 年起,所有的古代浏览器都是应用标记革除法。

总结

文本次要介绍一下 js 中垃圾回收机制和两种垃圾回收算法,心愿大家看完后对此有个简略的概念,其中比拟重要的是辨析各种满足垃圾回收机制的情景,还有就是 对象有被援用不等于可拜访(相互援用)对于垃圾回收机制其实还有蛮多的问题须要摸索,例如:

  1. 标记革除法的具体实现
  2. 垃圾回收执行的机会和频率是怎么样的
  3. 有哪些优化机制保障革除效率

会在此系列的后续文章上进一步更新阐明。(应该会吧~)

常规:如果内容有谬误的中央欢送指出(感觉看着不了解不难受想吐槽也齐全没问题);如果有帮忙,欢送点赞和珍藏,转载请征得批准后著明出处,如果有问题也欢送私信交换,主页有邮箱地址

而后还有一些想说的就是,很感激大家的关注和一些私信,尤其是看到一些读者说看了文章当前的确有帮忙的,会感觉很打动。其实本人也满羞愧的,每次更新都间很久,的确有点太懒了~,当前会尽量勤快一些。

顺便再说下,RingCentral 目前在杭州也设置了办公点,而且能够申请长期近程办公,帮你辞别 996,工作生存两不误,有趣味的同学能够私信或者发邮件给我,能够收费帮忙内推~

参考文章

http://jayconrod.com/posts/55/a-tour-of-v8-garbage-collection

https://javascript.info/garbage-collection

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Memory\_Management

退出移动版