什么是内存泄露内存泄露是指一块被分配的内存既不能使用,又不能回收,直到浏览器进程结束。在C++中,因为是手动管理内存,内存泄露是经常出现的事情。而现在流行的C#和Java等语言采用了自动垃圾回收方法管理内存,正常使用的情况下几乎不会发生内存泄露。浏览器中也是采用自动垃圾回收方法管理内存,但由于浏览器垃圾回收方法有bug,会产生内存泄露。 内存泄露Quick View不同的浏览器中存在各种内存泄露方式,目前发现的主要是这样几种: 1. 循环引用已经确认存在泄漏的浏览器:IE6.0 FF2.0 含有DOM对象的循环引用将导致大部分当前主流浏览器内存泄露 这里有两个简单的概念 引用:a.属性=b,a就引用了b 循环引用:简单来说假如a引用了b,b又引用了a,a和b就构成了循环引用。 a和b循环引用: ![]() ![]() ![]() ![]() ![]() a循环引用自己: ![]() ![]() ![]() 循环引用很常见且大部分情况下是无害的,但当参与循环引用的对象中有DOM对象或者ActiveX对象时,循环引用将导致内存泄露。我们把例子中的任何一个new Object替换成document.getElementById或者document.createElement就会发生内存泄露了。 function bindEvent()
{ var obj=document.createElement("XXX"); obj.onclick=function(){ //Even if it's a empty function } } 这个bindEvent执行时100%会发生内存泄露,Someone 可能会问,哪里出现了循环引用? 关于closure和scope chain参与的循环引用比较复杂,此处暂不深入讨论。有一个简单的判断方式:函数将间接引用所有它能访问的对象。obj.onclick这个函数中 可以访问外部的变量obj 所以他引用了obj,而obj又引用了它,因此这个事件绑定将会造成内存泄露。在IBM的文章中介绍了2种方式解决类似的问题一个是obj=null,另一个是把onclick的函数写在bindEvent外,重复人家的我就不说了。简单贴下代码: function bindEvent()
{ var obj=document.createElement("XXX"); obj.onclick=onclickHandler; } function onclickHandler(){ //do something }
function bindEvent()
{ var obj=document.createElement("XXX"); obj.onclick=function(){ //Even if it's a empty function } obj=null; }
function bindEvent()
{ var obj=document.createElement("XXX"); var var0="OOXX";//Here is a variable obj.onclick=function(){ alert(var0);//I want to visit var2 here! } return obj;//bindEvent must return obj! } 好了 这下两种办法都不行了,假如我把函数写外面去,var0肯定访问不了,假如我把obj弄成null,还怎么return它呢?这并不是空想的需要,这实际 上是一个用JS定制DOM控件的简单抽象:创建DOM元素、设置私有属性、绑定事件。所以,我们必须update一下两个方法。首先,方法1,为了让函数 能访问某些变量,我们可以通过一个Builder函数来订制onclick的外部闭包: function bindEvent()
{ var obj=document.createElement("XXX"); var var0="OOXX";//Here is a variable obj.onclick= onclickBuilder(var0);//想访问谁就把谁传进去!! return obj;//bindEvent must return obj! } function onclickBuilder(var0)//这里跟上面对应上就行了 最好参数名字也对应上 { return function(){ alert(var0); } } 第二个办法,这个来自51js的chpn同学,让obj=null在return 之后执行!! function bindEvent()
{ try{ var obj=document.createElement("XXX"); var var0="OOXX";//Here is a variable obj.onclick=function(){ alert(var0);//I want to visit var2 here! } return obj;//bindEvent must return obj! } finally { obj=null; } }
2. 某些DOM操作这是IE系列的特有问题 简单的来说就是在向不在DOM树上的DOM元素appendChild,可能会发生内存泄露(只是可能,具体原因不明,似乎十分复杂,下面例子中去掉onClick也可以避免泄露)。所以appendChild的顺序可能影响内存泄露,来自微软的例子: ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]()
首先我们定义一个看不见的元素当作垃圾箱,所有新创建的元素都扔进垃圾箱里,这样保证了所有DOM元素都在DOM树上,IE7就可以正确回收了,另一方面也能避免所谓的"appendChild顺序不对导致内存泄露"。 function MemoryFix(){
var garbageBox=document.createElement("div"); garbageBox.style.display="none"; document.body.appendChild(garbageBox); var createElement=document.createElement; document.createElement=function(){ var obj=Function.prototype.apply.apply(createElement,[document,arguments]); garbageBox.appendChild(obj); return obj; } } 3. 自动类型装箱转换别不相信,下面的代码在ie系列中会导致内存泄露 var s=”lalala”;
alert(s.length); s本身是一个string而非object,它没有length属性,所以当访问length时,JS引擎会自动创建一个临时String对象封装s,而这个对象一定会泄露。 var s="lalala";
alert(new String(s).length);
参考Understanding and Solving Internet Explorer Leak Patterns(中文版) Memory leak patterns in JavaScript(中文版) 51js的一则讨论 |
|
来自: actinia > 《javascript》