一、 产生内存泄露的原因
在内存管理方面,JavaScript采用的是自动垃圾回收机制。简单地说,当我们在JavaScript中创建一个对象时,脚本引擎会负责将内存分配给对象,当引擎发现当前堆栈中没有对该对象的引用时,就会将该对象进行垃圾回收(即销毁)。我们可以通过将一个对象的引用置为空(null),显式的将一个对象销毁并回收。
和其他高级语言一样,JavaScript本身提供的这套垃圾回收机制本身并没有任何问题,在通常情况下,这套机制十分奏效。但是当我们将JavaScript与HTML DOM、XML DOM或者其他COM组件混合编写程序时(而在AJAX开发中通常如此),就会发生意想不到的内存泄漏问题。
Internet Explorer采用的是一种引用计数系统。在这套系统中,每一个HTML元素或者对象都具有一个引用计数,它记录了当前有多少个对象正在引用该元素,当计数为零的时候,它就会被自动销毁。这种引用计数系统在早期的Web应用开发看来似乎十分奏效,但是其实该方案本身就存在了一些盲点,而正是这些缺憾最终导致了著名的JavaScript内存泄漏,
二、 内存泄露的几种解决方式
1、使用平台提供的$attachEvent方法订购事件
HTML DOM提供了一套订购、退订事件的机制,是分别通过attachEvent和detachEvent这一对方法实现的,但是往往我们只订购事件,而忘了detachEvent,这也是容易引发循环引用的潜在隐患之一。
MWFrameowrk框架在底层扩展了$attachEvent全局方法,它会在窗体重新加载或关闭前将之前订购过的事件进行批量退订。该方法原型如下:
$attachEvent(element, eventName, handler);
该方法包含3个参数,其中,element参数为需要订购事件的DOM元素,eventName为事件名称,而handler则为事件处理函数。
例如:平台提供的FlowPanel.js中第114行代码:
me.internalTitle.attachEvent("ondragstart", function() { return false; }); |
可以修改为:
$attachEvent(me.internalTitle, "ondragstart", function() { return false; }); |
2、减少匿名函数的使用
查询一下匿名函数的信息【】】】】】】
例如:平台提供的PerformanceCounterClass.js第18行代码:
window.attachEvent("onload", function() { document.body.appendChild(_iframe); window.setTimeout(me.report, 1000); }); |
可以将匿名函数独立出一个有名字的函数,修改后的代码如下:
$attachEvent(window,"onload",appendIframe); function appendIframe() { document.body.appendChild(_iframe); window.setTimeout(me.report, 1000); } |
3 避免使用循环引用
当两个对象互相进行引用的时候,就产生了循环引用。例如下面的代码:
<script> var myGlobalObject; function SetupLeak()//产生循环引用,因此会造成内存泄露 { myGlobalObject= document.getElementById("LeakedDiv");// document.getElementById("LeakedDiv").expandoProperty = myGlobalObject; } </script> <body onload="SetupLeak()"> <div id="LeakedDiv"></div> </body> |
我们可以看到,myGlobalObject指向了一个DOM对象,而这个DOM对象的一个属性又指向了myGlobalObject,循环引用出现,造成了内存泄露。
解决方案很简单,在确保属性不被使用后,加入如下代码就可以了。
function BreakLeak()//解开循环引用,解决内存泄露的问题 { document.getElementById("LeakedDiv").expandoProperty = null; } |
以上这种循环引用是比较简单的一种,但是,当我们程序非常复杂的时候,发现和修改就没有那么容易了。
在技改大修模块中,为了方便用户使用,在项目名称上加入了超链接,用于打开项目的详细信息页面。代码如下:
dataGrid_new.columns["XMMC"].renderCell = function(dataCell, container) { dataCell.dataGrid = dataGrid_new; _renderXMCXMMCCell_DX(dataCell, container); }; |
这段代码存在着严重的内存泄露,而且随着数据列表中的数据条数增多,内存泄露会越来越严重。解决起来就比较复杂,为了最大程度的减少对代码的修改。干脆将该功能取消掉,用点击放大镜的方式来打开项目的详情信息页面。
4 页面退出时将全局变量置为null】】
在页面退出的时候,使用onDisposing事件,将全局变量置为null.
5 Interval】
将代码
window.setInterval(me.report, 15 * 1000); |
修改为:
var _iTimerInterval; _iTimerInterval = window.setInterval(me.report, 15 * 1000); |