概述
JS的垃圾回收机制是为了以防内存泄漏,内存泄漏的含义就是当已经不需要某块内存时这块内存还存在着,垃圾回收机制就是间歇的不定期的寻找到不再使用的变量,并释放掉它们所指向的内存。
C#、Java、JavaScript有自动垃圾回收机制,但c++和c就没有垃圾回收机制,也许是因为垃圾回收机制必须由一种平台来实现。在JS中,JS的执行环境会负责管理代码执行过程中使用的内存
变量的生命周期
当一个变量的生命周期结束之后它所指向的内存就应该被释放。JS有两种变量,全局变量和在函数中产生的局部变量。局部变量的生命周期在函数执行过后就结束了,此时便可将它引用的内存释放(即垃圾回收),但全局变量生命周期会持续到浏览器关闭页面。
JS垃圾回收方式
JS执行环境中的垃圾回收器怎样才能检测哪块内存可以被回收有两种方式:标记清除(mark and sweep)、引用计数(reference counting)。
标记清除
这是javascript中最常用的垃圾回收方式。当变量进入执行环境是,就标记这个变量为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到他们。当变量离开环境时,则将其标记为“离开环境”。
垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记。然后,它会去掉环境中的变量以及被环境中的变量引用的标记。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后。垃圾收集器完成内存清除工作,销毁那些带标记的值,并回收他们所占用的内存空间。
这个算法把“对象是否不再需要”简化定义为“对象是否可以获得”。
这个算法假定设置一个叫做根(root)的对象(在Javascript里,根是全局对象)。定期的,垃圾回收器将从根开始,找所有从根开始引用的对象,然后找这些对象引用的对象……从根开始,垃圾回收器将找到所有可以获得的对象和所有不能获得的对象。
引用计数
所谓"引用计数"是指语言引擎有一张"引用表",保存了内存里面所有的资源(通常是各种值)的引用次数。如果一个值的引用次数是0,就表示这个值不再用到了,因此可以将这块内存释放。
内存泄漏
意外的全局变量
比如 function(){ bar = '小猪' } 这里bar就没有被声明会变成一个全局的变量,再页面关闭之前不会被释放,还有
function foo(){
this.name = '小猪'
}
foo()
这种自己调用自己的也会this指向了全局对象 window
被遗忘的计时器或回调函数
计时器或者回调函数中使用了某个上面的变量。但是结束后计时器和回调函数没有被清除,那么他所使用的上面的变量也会一直被引用,从而不被垃圾回收机制所收回。
闭包
闭包可以维持函数内局部变量,使其得不到释放。
解决方式:将时间处理函数定义在外部,解除闭包,或者在定义事件处理函数的外部函数中,清除原本声明的变量。
没有清理的DOM元素引用
如果显示在某个地方,用document获取某个DOM元素,并且赋给某个值,就算是下面你在h5的页面中把那个节点给移除了,但是你的js代码中还是有这个dom,换而言之,DOM元素还存在内存里面。
内存泄漏的识别方法
步骤:打开开发者工具 Performance
勾选 Screenshots 和 memory
左上角小圆点开始录制(record)
停止录制
图中 Heap 对应的部分就可以看到内存在周期性的回落也可以看到垃圾回收的周期,如果垃圾回收之后的最低值(我们称为min),min在不断上涨,那么肯定是有较为严重的内存泄漏问题。
避免内存泄漏的一些方法
1、减少不必要的全局变量,或者生命周期较长的对象,及时对无用的数据进行垃圾回收。
2、注意程序的逻辑,避免死循环之类的
3、避免创建过多的对象
总而言之,不用的东西要及时的归还
垃圾回收的使用场景优化
数组array的优化
arr = [],这种方式虽然把原来的给释放了,但是又创建了一个新的空对象,实际上,将数组的长度赋值为0,arr.length = 0 能够达到清空数组的目的,并实现了数组的重用,减少内存垃圾的产生.
在循环的函数表达式,能复用的最好放在外面
// 在循环中最好也别使用函数表达式。
for(var k=0;k<10;k++){
var t=function(a){// 创建了10次 函数对象。
console.log(a)}
t(k)
}写成
// 推荐用法
function t(a){console.log(a)}
for(var k=0;k<10;k++){t(k)}
t=null