JS的垃圾回收机制

1,356 阅读4分钟

JS采用自己的一套垃圾回收算法进行自动的内存管理。

V8 内存限制

在64位系统下,V8最多只能分配1.4G, 在 32 位系统中,最多只能分配0.7G。V8 为什么要给它设置内存上限?是由两个因素所共同决定的

1、JS单线程的执行机制

2、JS垃圾回收机制的限制

因为JS是单线程运行的,所以一旦进入到垃圾回收,那么其它的各种运行逻辑都要暂停; 同时垃圾回收其实又是非常耗时间的操作,就会造成程序卡顿,影响性能。所以V8就限制了堆内存的大小。

堆内存由两部分组成:

1、新生代内存: 临时分配的内存,存活时间短。

2、老生代内存: 常驻内存,存活的时间长。

新生代的内存回收机制

新生代内存默认限制是:在 64 位和 32 位系统下分别为 32MB 和 16MB。

新生代内存空间均分为两部分,From 和 To。

1、From 是正在使用的内存。

2、To 是目前闲置的内存。

当进行垃圾回收时,V8将From部分的对象检查一遍,如果是存活对象那么复制到To内存中(在To内存中按照顺序从头放置的),如果是非存活对象直接回收即可。当所有的From中的存活对象按照顺序进入到To内存之后,From和To两者的角色对调,From现在被闲置,To为正在使用,如此循环。 对于新生代的对象,采用空间换取时间的Scavenge算法, 尽可能快的回收内存。

老生代内存的回收:

新生代内存默认限制是:在 64 位和 32 位系统下分别为 1400MB 和 700MB。 新生代中的变量如果经过2次回收后依然存在,那么就会被放入到老生代内存中,这种现象就叫晋升。 从新生代空间转移到老生代空间的条件:

1、经历过一次以上 Scavenge GC 的对象

2、当 to space 体积超过25%

处理老生代对象时,采用深度优先扫描,用三色标记的算法。

引用计数垃圾收集

这是最初级的垃圾收集算法。此算法把“对象是否不再需要”简化定义为“对象有没有其他对象引用到它”。如果没有引用指向该对象(零引用),对象将被垃圾回收机制回收。 简单地说,如果该对象每被引用一次,则+1,每被释放几次,则-1。 该算法有个限制:如果,两个对象被创建,并互相引用,形成了一个循环。它们被调用之后会离开函数作用域,所以它们已经没有用了,可以被回收了。然而,引用计数算法考虑到它们互相都有至少一次引用,所以它们不会被回收。

标记-清除算法

从2012年起,所有现代浏览器都使用了标记-清除垃圾回收算法。

标记-清除算法分为两个阶段,即标记阶段和清除阶段。标记存活的对象,未被标记的则被释放。 首先会遍历堆中的所有对象,标记存活对象,清除非存活对象。进行空间的回收。

当然这又会引发内存碎片的问题,存活对象的空间不连续对后续的空间分配造成障碍。老生代又是如何处理这个问题的呢? Mark-Compact算法整理内存碎片。在清除阶段结束后,把存活的对象全部往一端靠拢。由于是移动对象,它的执行速度不可能很快,事实上也是整个过程中最耗时间的部分。

增量标记 将标记任务分为很多小的部分完成,在代码执行间隙执行垃圾回收,如此循环,直到标记阶段完成。

并发标记 (重点 ): 现在Chrome 64和Node.js v10已经默认启用并发标记。并发标记允许标记行为与应用程序同时进行。

晚上看了神三元的文章,查了一些资料,整理了一下,如果有对原著冒犯,请联系我删除,谢谢,Thanks♪(・ω・)ノ

参考资料: juejin.cn/post/684490…

yq.aliyun.com/articles/60…