垃圾回收的定义
代码运行时,需要分配内存空间来储存变量,当变量不再参与运行时,就需要系统收回被占用的内存空间,这就是垃圾回收。
JS的垃圾回收机制
JS具有自动垃圾回收机制,会定期对那些不再使用的变量所占用的内存进行释放,原理就是找到不再使用的变量,然后释放掉其占用的内存。
JS的垃圾回收策略
- 标记清理(mark-and-sweep)
JS最常用的垃圾回收策略是标记清理。当变量进入上下文,比如在函数内部声明一个变量时,这个变量会被加上存在于上下文中的标记;当变量离开上下文时,也会被加上离开上下文的标记。垃圾回收程序运行的时候,会标记内存中存储的所有变量,然后它会将所有在上下文中的变量以及被在上下文中的变量引用的变量的标记去掉,在此之后再被加上标记的变量就是待删除的了,随后垃圾回收程序做一次内存清理,销毁带标记的所有值并收回它们的内存。 - 引用计数(reference counting)
另一种没那么常用的垃圾回收策略是引用计数。其思路是对每个值都记录它被引用的次数。声明变量并给它赋一个引用值时,这个值的引用数为1;如果同一个值又被赋给另一个变量,那么引用数加1;如果保存对该值引用的变量被其他值给覆盖了,那么引用数减1。当一个值的引用数为0时,就说明没办法再访问到这个值了,因此可以安全地收回其内存。垃圾回收程序下次运行的时候就会释放引用数为0的值的内存。
但是这种方法有循环引用的问题。所谓循环引用,就是对象A有一个指针指向对象B,而对象B也引用了对象A。
eg:
function problem() { let objectA = new Object(); let objectB = new Object(); objectA.someOtherObject = objectB; objectB.anotherObject = objectA; }在这个例子中,objectA和objectB通过各自的属性相互引用,意味着它们的引用数都是2。在标记清理策略下这不是问题,因为在函数结束后这两个对象都不在作用域中;而在引用计数策略下objectA和objectB在函数结束后还会存在,因为它们的引用数永远不会变成0,如果函数被多次调用,则会导致大量内存永远不会被释放。
如何减少垃圾回收
- 对对象进行优化
对象尽量复用。对于不再使用的对象,就将其设置为null,使其尽快被回收。 - 对数组进行优化
在清空一个数组时,最简单的方法就是给其赋值为[],但是与此同时会创建一个新的空数组,可以将数组的长度设置为0,以此来达到清空数组的目的。 - 对函数进行优化
对于在循环中的函数表达式,如果可以复用,尽量放在函数的外面。