共计 3111 个字符,预计需要花费 8 分钟才能阅读完成。
引子
我突然想起来了还有个博客账号,最近工作工作较少,所以间歇性踌躇满志又更新一下 -\_-
注释
背景介绍 - 什么是垃圾回收
垃圾回收 (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 个步骤:
- 标记:从根节点(这里的根节点,指的就是上文提到的 全局对象以及以后执行环境以及作用域链上可拜访的对象)登程,深度优先遍历所有能够拜访到的节点,并打上 ” 可拜访 ” 的标记
- 革除:从所有节点里,革除掉没有打上标记的节点。
这里介绍的是外围思路,具体实际的时候有些中央是能够优化的,例如第一步标记的过程,对于某些节点有可能被反复遍历的,就像后面提到的 ’ 朋友圈 ’ 模型中,既能够通过 circle
拜访其中的某个对象,也能够通过某个 user
的friend
属性拜访到,那么为了防止反复遍历,咱们就能够另外用一种标记,来标识示意节点曾经被遍历过,具体算法会在后续的系列文章介绍。
援用计数法
其实 mdn 还介绍了晚期的另一种回收算法,咱们也介绍提一下。
援用计数法顾名思义是就是 先记下某个变量被援用的总次数,而后当被援用次数为 0 时,就示意可回收。
这个思路咋一看和标记革除法是很类似的,然而实际上有个很显著的区别,就是在后面 相互援用 的例子中:两个 user
其实都是不可取得的,然而因为相互援用,它们的被援用次数并不为 0,那么依照援用计数法,这两个对象就不会被革除,这是有问题的,所以从 2012 年起,所有的古代浏览器都是应用标记革除法。
总结
文本次要介绍一下 js 中垃圾回收机制和两种垃圾回收算法,心愿大家看完后对此有个简略的概念,其中比拟重要的是辨析各种满足垃圾回收机制的情景,还有就是 对象有被援用不等于可拜访(相互援用)对于垃圾回收机制其实还有蛮多的问题须要摸索,例如:
- 标记革除法的具体实现
- 垃圾回收执行的机会和频率是怎么样的
- 有哪些优化机制保障革除效率
会在此系列的后续文章上进一步更新阐明。(应该会吧~)
常规:如果内容有谬误的中央欢送指出(感觉看着不了解不难受想吐槽也齐全没问题);如果有帮忙,欢送点赞和珍藏,转载请征得批准后著明出处,如果有问题也欢送私信交换,主页有邮箱地址
而后还有一些想说的就是,很感激大家的关注和一些私信,尤其是看到一些读者说看了文章当前的确有帮忙的,会感觉很打动。其实本人也满羞愧的,每次更新都间很久,的确有点太懒了~,当前会尽量勤快一些。
顺便再说下,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