??JS的垃圾回收机制都是由js引擎实现的,大部分浏览器都有自己的js引擎,所以各自实现的垃圾回收机制都略有不同。但是主要的策略就两种:
1、标记清除
标记阶段:把所有活动对象做上标记。
清除阶段:把没有标记的对象销毁。
缺点:没有被清除的对象位置不变,导致清楚后的空闲内存空间不连续(内存碎片)
2、引用计数
??一个值赋给了一个变量,这个值引用计数加一,又被赋给另一个变量就再加一(类推),另一个值要是覆盖了原本已经赋值的变量,则这个值引用计数减一。
??缺点:1、需要一个不知上限的计数器。2、循环引用无法回收
??分代式垃圾回收:分为新生代区域和老生代区域。新生代区域容量小,会保存存活较短、体积较小的对象。老生代反之。不同分区的垃圾回收机制与回收频率都不同。
1、新生代区
??新生代区又分为使用区和空闲区,通过 Scavenge
算法把使用区活着的对象复制到空闲区并排序(解决内存碎片化问题),随后清理使用区剩余未标记的垃圾,此时使用区变成空闲区,空闲区变成使用区。上述过程反复,一个对象多次复制后仍存活就直接移到老生代区,就像两个杯子,一杯水倒来倒去,一直没撒出去的就是存活较久的,可以移到老生代区。或者当对象复制到空闲区,内存超过空闲区的25%时,也会直接移到老生代区。
2、老生代区
??老生代区使用标记清除(Mark-Sweep
),从根节点(dom、window、global)遍历标记活着的对象,回收器定时清理死亡的对象。使用标记整理算法把标记的对象向内存一端移动(解决标记清除的内存碎片化问题)
??tip: Scavenge
只复制活着的对象,Mark-Sweep
只清除死亡的对象。活对象在新生代区较少,死对象在老生代中交少,这是两种回收方式高效的原因。
3、并行回收
??js是单线程,所以垃圾回收会阻塞js脚本的执行,并行回收就是开启辅助线程帮助垃圾回收。
??缺点:像老生代区活着的对象大而多,就算是并行任务,一次的清理任务也可能过重导致阻塞过长,所以需要使用增量标记和惰性清理。
4、增量标记
??把回收任务分成一小步一小步的,执行完一小步再把主线程交给js,这样交替增量执行完一次标记任务,但可能会出现执行任务程序时内存中标记好的对象引用关系被修改的问题,所以需要引入三色标记法和写屏障法来解决。
5、惰性清理
??在内存充足情况先优先执行js代码,主线程空闲时才清理垃圾,无需一次去清理完未标记的对象,逐一清理,配合增量标记使用
??缺点:增量标记后惰性清理,没有减少占用主线程的时间,甚至增加了。
6、并发回收
??完全在辅助线程执行,不会占用主线程。
??缺点:需要考虑垃圾回收器在辅助线程已经做的标记或者正在做标记时,js对象引用的改变,需要额外实现读写锁机制。
??v8垃圾回收机制就是根据上述方法的优缺点,结合使用。
参考:
1、《深入浅出nodejs》5.1- v8的垃圾回收机制与内存限制
2、掘金:https://juejin.cn/post/6981588276356317214?share_token=6cecbce3-8f54-4e4a-9049-d73399fa76e8