Javascript内存管理
在编写Javascript程序时,开发人员不用关心内存问题,内存分配及无用内存的回收完全实现了自动化管理。垃圾收集器会按照预定的时间间隔,
周期性的找出那些不再继续使用的变量,然后释放其所占用的内存。具体到浏览器中,用于标识无用变量的策略,通常有两种:标记清除和引用策略。
标记清除
标记清除策略是Javascript中最常用的垃圾收集方式,截止2008年,IE、Firefox、Opera、Chrome和Safari采用的都是标记清除方式或者类似的策略,只不过垃圾收集的间隔时间有所不同。
标记清除的原理很容易理解,当变量进入一个执行环境时,将这个变量标记为“进入环境”,当变量离开环境时,则将其标记为“离开环境”。垃圾收集器在 运行的时候会给存储在内存中的所有变量都加上标记,然后它会去掉当前环境中的变量以及被环境中的变量引用的变量的标记,而在此之后再被加上标记的变量将被 视为准备删除的变量,原因是环境中的变量已经无法访问这些变量了。最后,垃圾收集器完成内存清除的工作,销毁那些带标记的值并回收他们占据的内存空间。
引用计数
引用计数的含义是跟踪记录每个值被引用的次数。当声明一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数便是1,如果同一个值又被赋给另 一个变量,则该值的引用次数加1,相反,如果包含对这个值引用的变量又取得了另一个值,则这个值的引用次数减1。当这个值的引用次数为0时,说明没有办法 访问到它了,因而可以将其占用的内存空间回收。
引用计数策略遭遇的一个严重问题是循环引用,请看下面的例子:
function memoryProblem () { var o1 = new Object(); var o2 = new Object(); o1.prop = o2; o2.prop = o1; }
上面的代码中,当函数memoryProblem执行完毕后,o1和o2的引用次数不为0,最后得不到垃圾收集器的内存回收。
内存泄漏
当前主流浏览器的垃圾回收均采用标记清除方式来进行管理,但有一个非常特殊并且我们不得不面对的就是IE浏览器的对象并不全是原生的 Javascript对象,其BOM和DOM对象是使用C++以COM对象的形式实现的,这部分对象的内存管理相对于Javascript原生对象是独立 的。问题的出现正是因为COM对象的内存回收是采用引用计数策略而非标记清除方式。
如果我们定义的Javascript对象与DOM或BOM对象之间形成循环引用,在IE下则会发生内存不能被正确回收的问题,这也是最常见的IE内存泄漏。如下面的例子:
var elem = document.getElementById('elemId'); var o = new Object(); o.prop = elem; elem.attr = o;
上面的代码中,DOM对象elem与Javascript对象o之间产生了循环引用,即使将elem从页面中移除(removeChild或者replaceChild),其所占用的内存也不会被回收。
解决办法就是,在不使用这些变量时手工解除Javascript对象和DOM对象的引用,比如:
o.prop = null; elem.attr = null;