在聊垃圾回收机制之前,我们需要先知道一个基础概念,在js中什么是垃圾,下面是我的理解:
如果一个变量、函数、对象在js内存中不再使用了,那么就会被标记成垃圾
比如:
1.已经调用完毕的函数,及函数内的变量(闭包除外)等等
2.值为null的
3.无法被访问到的值,也就是从根(全局global)上出发,没有节点引用到的地方
为什么要垃圾回收呢?
因为如果js不垃圾回收,这些变量都是占用js内存的,越来越多的话,程序就会卡死。
垃圾回收有两种方式:
1.引用计数法:
实现方式就是一个变量或对象等如果存在其他地方引用它,那么就给他计数,有几个引用就加几,如果后面不引用了,那么计数就减一,直到计数为0的时候,那么就会被GC(垃圾回收机制(Garbage Collection))回收
这种方法有个缺点循环引用:
const obj1 = {name:'hh'}
const obj2 = {name:'dd'}
obj1.a = obj2
obj2.a = obj1
两个对象内的属性相互引用,这样gc就永远清除不了了
2.标记清除法
什么是根?
根就是js固有的一些变量或函数,对象等,比如全局变量,window下的一些函数,对象,这些是不会被垃圾回收的
标记清除的实现方式就是从根出发, 根据它的引用判断哪些对象节点引用了它,然后那些对象节点又引用了哪些对象,这些都会被标记,最后那些没有标记的就被定义成了垃圾,需要被回收 比如下面红色圈圈内的就是要被删除的,因为它没有被global引用或者global下的对象引用,即使它们三个互相引用也不行
这种方式有几个问题:
1.需要占用大量的js时间去遍历,如果对象太多的话就可能会导致卡顿。
2.有可能在你遍历过程中这个对象被修改了。
针对上面问题有几点优化建议:
1.分代回收
分代回收就是将对象等分为旧代和新代,旧代就是从新代晋升的‘长寿’对象,新代就是新创建的对象,因为新代存在时间比较短,所以我们需要间隔比较短的时间去检查一下,旧代就可以间隔长一点时间去检查。
2、增量收集
可以将原本的10000个对象分成10份或者更多,分成几部分去标记回收,这样只会带来微小的延迟,效果也会比一次标记10000个对象好。
3.闲时收集
利用cpu空闲时间去收集,减少对代码运行的影响
以上是我对垃圾回收机制的一点点理解。