1.首先要明确什么是垃圾?
在JS中认定一样东西是垃圾的几种方法(一般认为没有被引用的就是垃圾):
- 所有全局变量都不是垃圾
- 局部变量在这个函数执行完之后就变成了垃圾
-
如果存在双引用,删了其中一个对象还是不会变成垃圾的,因为还有另外一个对象在引用它
-
环引用(别人引用你才有用,你引用别人是没有用的,对外界至少有一个桥梁,不要成为孤岛)
2.垃圾回收的必要性
由于字符串、对象和数组没有固定大小,所有当他们的大小已知时,才能对他们进行动态的存储分配。JavaScript程序每次创建字符串、数组或对象时,解释器都必须分配内存来存储那个实体。只要像这样动态地分配了内存,最终都要释放这些内存以便他们能够被再用,否则,JavaScript的解释器将会消耗完系统中所有可用的内存,造成系统崩溃。
引自《JavaScript权威指南(第四版)》
3.浏览器是如何找到这些垃圾并且被回收的?
- 标记-清除算法
步骤:
- 垃圾回收器获取根并“标记”(记住)它们。
- 然后它访问并“标记”所有来自它们的引用。
- 然后它访问标记的对象并标记它们的引用。所有被访问的对象都被记住,以便以后不再访问同一个对象两次。
- 以此类推,直到有未访问的引用(可以从根访问)为止。
-
除标记的对象外,所有对象都被删除。
缺点:JS是单线程的,在标记过程中,JS不能执行,如果有人把标记的删了,JS怎么解决?
改进方法: - 新生代和老一代(垃圾回收的时间不一样)
- 增量收集(例如:先收集1000个不中断,执行JS,然后继续收集一定的个数)
- 空闲时间收集
- 引用计数算法
记录每个对象被引用的次数,每次新建对象、赋值引用和删除引用的同时更新计数器,如果计数器值为0则直接回收内存。 很明显,引用计数最大的优势是暂停时间短
优点
- 可即刻回收垃圾
- 最大暂停时间短
- 没有必要沿指针查找,不要和标记-清除算法一样沿着根集合开始查找
缺点 - 计数器的增减处理繁重
- 计数器需要占用很多位
- 实现繁琐复杂, 每个赋值操作都得替换成引用更新操作
- 循环引用无法回收
4.前端还有特殊性(不仅有JS进程,还有DOM进程)
在浏览器里面,所有全局变量都没有引用也不代表这个东西就可以删除,因为有可能dom元素引用了这个函数本文参考链接:
https://segmentfault.com/a/1190000018605776?utm_source=tag-newest
https://www.jianshu.com/p/a8a04fd00c3c