一、内存泄漏
1.所谓内存泄漏,是指浏览器的垃圾回收机制无法正常回收没用的DOM对象,根本原因是DOM对象的引用数大于0。2.在IE9之前的版本,IE浏览器的DOM对象和JS对象使用了不同的垃圾回收机制。这是内存泄漏的根源。
3.浏览器回收DOM对象,都是查看DOM对象的引用次数是否为0。而有些时候,某个DOM对象的引用次数永远都不会变为0,哪怕是页面已经关闭了。想让内存发生泄漏,做法就是让DOM对象的引用数始终大于0。
4.关于这四种泄漏的具体描述,还是请各位参照原文:http://msdn.microsoft.com/en-us/library/Bb250448。
二、内存泄漏的种类
1.循环引用(Circular References)
(1)这是一种非常常见的形式。(2)循环引用,最简单的说法就是定义了DOM对象的某个属性指向其自身。
- //小实验
- <div id="myDiv"></div>
- <script language="javascript">
- var box=document.getElementById("myDiv");
- document.getElementById("box").myProperty=box;
- </script>
(4)如果这种两个引用的循环很容易看出来,那么换成几十个DOM和Object的集合,循环就不容易看出来了。就算寻找的话,也相当于在有向图里寻找回路,算法复杂度相当高。
2.闭包(Closures)
(1)与循环引用不同,闭包不是通过两个毫不相干的对象构造循环,而是通过引入父函数的局部变量构造循环。(2)前面提到过,闭包的作用域链中,保留着对父级函数活动对象的引用,开发时,不知不觉就会出现循环。
- //小实验
- <div id="myDiv"></div>
- <script language="javascript">
- function bindClick(){
- var element=document.getElementById("myDiv");
- element.addEventListener("click",clickHandler,false);
- function clickHandler(){
- alert(element.id);
- }
- }
- bindClick();
- </script>
3.页面交叉泄漏(Cross-Page Leaks)
(1)在使用js动态创建添加DOM对象时,添加顺序不对,在某种情况下会引起这种泄漏。主要是因为DOM对象创建过程中的临时对象不能即使得到清理造成的。比如下面这个例子。- //小实验
- function addDOM(){
- var outer=document.createElement("<div onClick='foo()'>");
- var inner=document.createElement("<div onClick='foo()'>");
- outer.appendChild(inner);
- document.body.appendChild(outer);
- document.body.removeChild(outer);
- outer.removeChild(inner);
- outer=null;
- inner=null;
- }
(3)对于这种普遍说法,我理解得不是很透彻,需要找其他资料看一下,尤其是appendChild的具体实现过程。不过大概理解就是,由于outer还没添加到document,那么直接将inner添加到outer时,会产生一个对象描述他们的父子关系;而当outer被添加到document后,父子关系的描述就使用了document自带的另一个对象,然后创建的临时的描述父子关系的对象就泄漏了。
4.貌似泄漏(Pseudo-Leaks)
(1)在大多数时候,一些API的实际的行为和它们预期的行为可能会导致你错误的判断内存泄漏。(2)这个问题也需要依赖创建临时对象来产生"泄漏"。对一个脚本元素对象内部的脚本文本一而再再而三的反复重写,慢慢地你将开始泄漏各种已关联到被覆盖内容中的脚本引擎对象。
- //小实验
- <html>
- <head>
- <script language="javascript">
- function LeakMemory(){
- for(i = 0; i < 5000; i++){
- hostElement.text = "function foo(){}";
- }
- }
- </script>
- </head>
- <body οnlοad="LeakMemory()">
- <script id="hostElement">function foo(){}</script>
- </body>
- </html>
三、内存泄漏的解决
1.循环引用。如果规定了DOM的属性,可以在网页关闭时或者定期执行斩链过程。理论上如果多个对象只存在一个循环,断开一个就可以了。但正如前面说的,这个循环可能会非常复杂,如果是图的话,砍断哪个最有效很难说。因此最简单的办法就是将诸如下面例子里的DOM属性统统砍断。- //小实验
- function breaklinks(){
- document.getElementById("box").myProperty = null;
- }
- //小实验
- <div id="myDiv"></div>
- <script language="javascript">
- function bindClick(){
- var element=document.getElementById("myDiv");
- var id=element.id;
- element.addEventListener("click",clickHandler,false);
- function clickHandler(){
- alert(id);
- }
- element=null;
- }
- bindClick();
- </script>
- //解法1
- function addDOM(){
- var outer=document.createElement("<div onClick='foo()'>");
- var inner=document.createElement("<div onClick='foo()'>");
- document.body.appendChild(outer);
- outer.appendChild(inner);
- outer.removeChild(inner);
- document.body.removeChild(outer);
- outer=null;
- inner=null;
- }
- //解法2
- function addDOM(){
- var outer=document.createElement("<div>");
- var inner=document.createElement("<div>");
- document.body.appendChild(outer);
- outer.appendChild(inner);
- outer.οnclick=foo;
- inner.οnclick=foo;
- outer.removeChild(inner);
- document.body.removeChild(outer);
- outer=null;
- inner=null;
- }