内存泄露是一个非常重要的一个问题,特别是对于JavaScipt,如果占用内存过大,将使整个浏览器的速度拖慢,造成一个很不好的用户体验。
内存泄露造成的原因在于没有及时的进行垃圾回收,或者只要页面存在,就不会进行垃圾回收。例如全局变量等等。
首先了解一下,关于垃圾回收机制,分为两种,标记清除法和引用计数法。
标记清除法,一个函数局部作用域,当这个函数调用的时候,内部定义的局部变量将进入运行环境。当函数调用完毕之后,这个变量就会被标记,当下一次垃圾清除的时候,一起删除。
引用计数法,引用计数的含义是跟踪记录每个值被引用的次数。当声明一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数便是1,如果同一个值又被赋给另 一个变量,则该值的引用次数加1,相反,如果包含对这个值引用的变量又取得了另一个值,则这个值的引用次数减1。当这个值的引用次数为0时,说明没有办法 访问到它了,因而可以将其占用的内存空间回收。(JavaScript 高级程序设计第二版)。当退出作用域的时候,然后减1;
然后我们来看一下,内存分配方式。
静态分配:全局变量,函数之类的分配方式,由于他们页面没有关闭之前,他们是不会被清除的。
自动分配:局部变量,但不是所有局部变量,比如说自带的类型,只要不是动态分配的局部变量就是自动分配的。满足两个条件,
动态分配:使用new创建出来的,主动要求给分配空间的。
我们谈一下,内存泄露常见的一些地方吧,基本上都是引用计数法搞的。
1:相互引用/循环引用
这样的结果是什么,当然是a,b的技术永远也不会变成0。所以说,当我们不需要的时候,解引用。添加上下面两句。
- a.b=null;
- b.a=null;
我们这里只是举了两个对象,当三个甚至更多的时候,更要注意。
2:自引用
3:事件绑定
一般来说,事件绑定是没有多少问题的。但是问题在于,如果你把这个DOM移除了,但是之前的事件监听仍然还在内存之中,之后就无法消除这个泄露问题了。
解决办法,就是在DOM移除之前就需要把这个事件绑定给移除掉。
4:闭包
- function leak(){
- var o = {};
- function closure(){
- }
- o.f = closure;
- }
我们从上面图上可以看出来,leak里面引用这o,而o.f引用closure,closure的环境位于leak,这就变成一个相互引用。对于这个东西来说,就要把函数放到外面来。
总体来说,如果减少内存泄露的话,就尽量减少相互引用,没有用的时候解引用,不要一直占据这内存。
并不是你关闭了这个页面或者页面跳转,内存泄露就不存在了,除非你关闭了整个浏览器
下面有两个小工具,用来检测内存泄露问题
SIEve
Leak Monitor