概念
垃圾回收机制:执行环境负责管理代码在执行过程中使用的内存。
js垃圾回收机制是为了以防内存泄漏,内存泄露的含义就是我们不需要的某块内存这时候还在内存中存放着,没有被释放,导致它占用的这部分内存无法被使用,垃圾回收机制会定期(周期性)找出哪些不再继续使用的变量,然后与释放其内存。
这里的垃圾指的是什么呢?
-
没有被引用的变量或者对象
-
无法访问到的对象(几个对象相互嵌套,互相引用)
知道什么是垃圾后,js引擎就会出来进行工作,把这些垃圾占用的内存空间进行回收,这就叫做js的垃圾回收。
工作原理
当变量进入环境时,将这个变量标记为"进入环境"。
当变量离开环境时,则将其标记为"离开环境"。标记"离开环境"的就回收内存。
为什么要进行垃圾回收
javascript程序在每次创建字符串、数组或对象的时候,解释器首先都要先进行内存的分配才能对这个实体进行存储。像这样就是动态的分布了内存,最终这些被占用的内存都要经过释放之后才能够被再次利用,否则如果不断的进行存储,将会消耗完系统中所有可存储的空间,最终导致系统的崩溃。
javascript有自己的垃圾回收机制,javascript的解释器可以对存储的内存进行检测,当它确定一个对象不会再被用到的时候,就可以把这个对象所占用的内存释放掉。
var a = '我是num1'
var b = '我是num2'
var a = b //将a重新赋值 将b的值赋值给a
这段代码执行完毕之后,'我是num1'这个字符串就不会再次被用到,这时候就启动了垃圾回收机制,释放了'我是num1'字符串的存储空间,以便这部分空间能够被重复利用。
什么情况下会引起内存的泄露
虽然有垃圾回收机制,但是如果我们编写的代码操作不当还是会造成内存泄露的问题
1.意外的全局变量引起的内存泄漏
原因:全局变量,不会被回收
解决:使用严格模式进行避免
2.闭包引起的内存泄漏
原因:闭包可以维持函数内的局部变量,但是其得不到释放
解决:将事件处理函数定义在外部,解除闭包,或者再定义事件处理函数的外部函数中,删除对dom的引用
3.没有清理的DOM元素引用
原因:虽然别的地方删除了,但是对象中还依然存在着对dom的引用
解决:手动删除
4.被遗忘的定时器或者回调
原因:定时器中有dom的引用,即使dom删除了,但是定时器还在,所以内存中还是有这个dom。
解决:手动删除定时器和dom。
5.子元素存在引用引起的内存泄漏
原因:div中的ul li 得到这个div,会间接引用某个得到的li,那么此时因为div间接引用li,即使li被清空,也还是在内存中,并且只要li不被删除,他的父元素都不会被删除
垃圾回收的方式
1.标记清除
这是javascript最常用的垃圾回收方式
-
当变量进入执行环境时(函数中声明变量),就标记这个变量为“进入环境”,当变量离开环境时(函数执行结束),则将其标记为“离开环境”,离开环境之后还有的变量则是需要被删除的变量。
-
垃圾回收器在运行的时候会给存储在内存中的所有变量都加上标记。
-
去掉环境中的变量以及被环境中变量引用的变量的标记。(这些被去除标记的是不能回收清除的那些变量)
-
之后再被加上标记的变量即是需要回收的变量(因为环境中的变量已经无法访问到这些变量)
-
最后,垃圾收集器完成内存清除工作,销毁那些带标记的值,并回收他们所占用的内存空间。
优点
可以找到一些局部作用域的垃圾,可以解决引用计数算法中无法回收循环引用的问题
缺点
在清除垃圾之后,剩余对象的内存位置是不变的,就会导致空闲内存空间不连续,这样就出现了内存碎片,并且由于剩余空间不是整块,就需要内存分配的问题
2.引用计数
这种方式会引起内存的泄露,还存在较大的兼容性问题。
在这种机制中,系统会跟踪每个值被引用的次数。当一个变量被声明并赋予一个引用类型的值时,该值的引用次数加1。当这个变量指向其他值时,原来的值的引用次数减一。当某个值的引用次数变为零时,表示没有任何变量指向它,系统可以安全地回收该值所占用的内存空间,以供其他变量使用。这个机制的实现可以帮助管理内存,防止内存泄漏,并提高程序的性能。
引用计数方法可能导致循环引用,类似死锁,导致内存泄露。
function problem() {
var objA = new Object();
var objB = new Object();
objA.someOtherObject = objB;
objB.anotherObject = objA;
}
objA和objB相互引用,两个对象的引用次数都是2。函数执行完成之后,objA和objB还将会继续存在,因为他们的引用次数永远不会是0。这样的相互引用如果说很大量的存在就会导致大量的内存泄露。
引用计数优点:
-
发现垃圾时立即回收
-
最大限度减少程序暂停(当内存快满的时候就立刻去找引用计数为0的删掉)
引用计数的缺点:
-
造成比较大的性能开销
-
造成循环引用的问题