我突然想起来了还有个博客帐号,最近工做任务较少,因此间歇性踌躇满志又更新一下-\_-javascript
垃圾回收(garbage-collection)是指对已分配的内存进行回收,当咱们在建立一些变量,函数,对象时,例如:java
var a = "Hello"; var getName = function (){ //函数体}
都须要分配内存,而当这些值再也不被使用的时候,js就须要在合适的时候将这部分的内存进行回收,这就是垃圾回收机制,对于一些大型应用程序来讲,垃圾回收能够有效提升性能。在js里,执行垃圾回收是自动执行的,不对外提供任何接口,不过仍是有必要适当了解下它的原理。算法
了解完垃圾回收的概念之后,首要的问题就来了:对于已分配出去的内存,什么状况下能够进行垃圾回收呢?浏览器
联系下实际生活:咱们会把什么样的东西送去回收呢,那固然是肯定之后根本用不到的东西。闭包
在js里,也是同样,对于再也没法访问到的值,咱们就要进行回收。函数
举个简单例子:post
var user = { name: 'Leo' }; // 第一步,建立一个对象并把内存地址赋值给user user = null; // 第二步,修改user的内存地址
以前在其余文章已经说过,建立引用类型值的时候,赋值给变量的实质上是内存地址。执行上述代码的第一步以后,能够经过window.user
访问到{name: 'Leo'}
对象,性能
可是在执行Leo = null
以后,这个变量对象其实已经没法访问到了。也就达到了能够被回收的条件。优化
再看个稍微复杂的案例:spa
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