[javascript]
var div=document.getElementById("div");var first=div.firstChild,next=first;while(next){ var d=document.createElement("div");d.appendChild(next);d.innerHTML="";next=div.firstChild;}/////简单的removeChild方式:var div=document.getElementById("div");var first=div.firstChild,next=first;while(next){ next.parentNode.removeChild(next);next=div.firstChild;}[/javascript]但是经过使用Drip工具(测试IE是否内存泄漏的工具,),测试还是存在内存泄漏的问题,但是使用IE JS Leaks Detector却啥也检测不出来(全部的测试都检测不出来,就连网上都吹捧的内存泄漏的方式也检测不出来),还有使用了话说是Drip的增强版的sIEve(),也测试不出来。既然这样,那就暂且信任Drip吧。下面几种传说中的内存泄漏的方式都是在Drip下测试的。
在开始讲述之前,先大概了解一下javascript的GC机制:
垃圾回收进程尝试推断何时可以安全地回收不再使用的变量,通常是通过判定程序是否能够通过变量之间形成的引用网络到达该变量。当确信变量是不可达的,就在它上面标上可以回收的记号,并且在回收器的下一次清理中(可能在未来的任意时刻)释放相关的内存。
也就是说,垃圾回收机制会定时的检查程序中的对象,查看它是否跟别的对象之间已经完全断开了引用链而“孤单一人”,这时,垃圾回收机制就会回收这个对象的内存,否则,将不会回收。所以说,对象在使用完了之后,就应该被回收内存,而不是一直占用着内存不放,导致浏览器的内存使用量节节飙升。
第一种:既然上面谈到了关于removeChild,那就从它开始吧,通过Drip测试,简单的使用removeChild删除子节点的方式确实存在内存泄漏,但是使用了上面EXT使用的方式,也还是存在。经过一番搜索,有文章说需要清除节点的全部属性来实现内存的正确回收,那就进行了下面的测试。结果通过将节点的属性都delete掉之后,Drip显示没有内存泄漏了。
[javascript]
var div=document.getElementById("div");var first=div.firstChild,next=first;while(next){ div.removeChild(next); for(var k in next){ delete next[k]; } next=div.firstChild;}[/javascript]第二种:将一个DOM对象和一个JS对象相互成为对方的属性。对于这点,IE官方也都有说法:在IE6中,对于javascript object内部,jscript使用的是mark-and-sweep算法,而对于javascript object与外部object(包括native object和vbscript object等等)的引用时,IE 6使用的才是计数器的算法。也就是说,IE 6对于纯粹的Script Objects间的Circular References是可以正确处理的,可惜它处理不了的是JScript与Native Object(例如Dom、ActiveX Object)之间的Circular References。所以,当我们出现Native对象(例如Dom、ActiveX Object)与Javascript对象间的循环引用时,内存泄露的问题就出现了。当然,这个bug在IE 7中已经被修复了。(Fuck,难怪我用Drip测试不出来(系统是IE8的内核))。下面是我的一个测试:
[javascript]
function Encapsulator(element){ this.elementReference = element; element.expandoProperty = this;}function SetupLeak2(){ var obj=new Encapsulator(document.getElementById("test")); document.body.removeChild(document.getElementById("test")); //alert(document.getElementById("test").expandoProperty); 出现错误//说明从element.expandoProperty —> obj的引用已经断开了//但是从obj.elementReference到element的引用依然存在,//这样的话在IE6下element就无法回收内存,但是其他浏览器的GC机制都会很好的处理了这个问题。document.body.appendChild(obj.elementReference);}[/javascript]第三种:将事件处理函数放在定义它的函数的内部。这种情况之前就看到过,回想下自己以前编写js的方式:外包一个自执行函数,里面定义闭包内的变量和功能函数,也不乏对事件处理程序的处理。这样是否会造成IE下的内存泄漏呢?下面是两个测试程序:
[javascript]
var test=function(){ var div=document.getElementById("test"); var i=0; while((i++) < 20){ (function(index){ var o=document.createElement("p");o.innerHTML="AAA";o.οnclick=function(){ alert("haha,leap");}div.appendChild(o);o.οnclick=null;div.removeChild(o);})(i); }}[/javascript][javascript]function addEvent(){ var div=document.getElementById("event"); div.οnclick=function(){ this.parentNode.removeChild(this); }}[/javascript]上面的一段程序也是从网上摘录下来做测试的,在闭包中动态生成一个div元素,并给它添加事件,事件处理程序写在闭包里面,也就是内涵在test函数里面,可是在removeChild的时候,Drip下显示还是内存泄漏了,即使是把它的onclick属性设置为null也不行。第二个测试程序中,在事件处理程序中通过removeChild删除当前节点的时候,也显示内存泄漏。
第四种:在创建DOM对象时插入script。这个还是第一次看到。即是通过createElement创建DOM元素的时候,直接在字符串中插入了js代码:document.createElement(“<div οnclick=’foo();’>”),但是这种方式只在IE下有效。通过测试下面的程序,在Drip中也确实显示内存泄漏了
[javascript]
var leakMemory=function(){ for(i = 0; i < 5000; i++){ var parentDiv = document.createElement("<div onClick=’foo()’>"); }}[/javascript]第五种:总是先将新创建的DOM对象插入到文档后,在对其进行其他操作。对于这点,我想象不到它是如何造成内存泄漏的。而且,它跟页面优化的一些方式可能存在冲突。在某些情况下,在创建了DOM元素之后,先处理DOM的操作,最后才插入到文档中,这样可以避免尽可能的由于reflow影响性能的情况。这可能就需要一个权衡了吧,因地制宜~
总结:
上面是本人通过使用Drip工具测试的结果,但是由于在sIEVE和JS Leaps Detector下测试都没发现内存泄漏的情况,所以纠结的很。经过这一番折腾,也不枉自己一番倒腾倒腾吧,在以后的编写代码中,可以或多或少的去避免这些不必要的可能造成内存泄漏的情况出现。
同时,如果有说错的地方,欢迎指正,共同学习~~
更多参考:《》,《》,《》,《》,《》,《》,《》,《》,《》,《》