当前位置:首页>>软件教程>>冲浪宝典>>新闻内容
解决IE的内存泄漏方式
作者: 发布时间:2006-6-12 10:37:14 文章来源:
      不得不说Eric Lippert同志疾呼的:Don't use closures unless you really need closure semantics. In most cases, non-nested functions are the right way to go. 是非常消极的应付之辞。  本文将讲书述losures引起的IE内存泄漏,在本文中Closures翻译成闭包或闭包函数。最近在网上看到一个对Closures的解释,它是这么说的:

<HTML>
<HEAD>
<script language="javascript">
function initpage()
{
 window.setTimeout("window.location.reload()", 500, "javascript");
}
</script>
</HEAD>
<body onload="initpage()" >
<div class='menu' id='menu'></div>
<script language='javascript'>
hookup(document.getElementById('menu'));
function hookup(element)
{
 element.attachEvent( "onmouseover", mouse);
 function mouse ()
 {}
}
</script>
</body>
</HTML>
In this code, the handler (the mouse function) is nested inside the attacher (the hookup function). This arrangement means that the handler is closed over the scope of the caller (this arrangement is named a "closure").

  闭包函数(Closures)

  由于闭包函数会使程序员在不知不觉中创建出循环引用,所以它对资源泄漏常常有着不可推卸的责任。而在闭包函数自己被释放前,我们很难判断父函数的参数以及它的局部变量是否能被释放。实际上闭包函数的使用已经很普通,以致人们频繁的遇到这类问题时我们却束手无策。在详细了解了闭包背后的问题和一些特殊的闭包泄漏示例后,我们将结合循环引用的图示找到闭包的所在,并找出这些不受欢迎的引用来至何处。

CircularReferences.gif
Figure 1. 闭包函数引起的循环引用

  普通的循环引用,是两个不可探知的对象相互引用造成的,但是闭包却不同。代替直接造成引用,闭包函数则取而代之从其父函数作用域中引入信息。通常,函数的局部变量和参数只能在该被调函数自身的生命周期里使用。当存在闭包函数后,这些变量和参数的引用会和闭包函数一起存在,但由于闭包函数可以超越其父函数的生命周期而存在,所以父函数中的局部变量和参数也仍然能被访问。在下面的示例中,参数1将在函数调用终止时正常被释放。当我们加入了一个闭包函数后,一个额外的引用产生,并且这个引用在闭包函数释放前都不会被释放。如果你碰巧将闭包函数放入了事件之中,那么你不得不手动从那个事件中将其移出。如果你把闭包函数作为了一个expando属性,那么你也需要通过置null将其清除。

  同时闭包会在每次调用中创建,也就是说当你调用包含闭包的函数两次,你将得到两个独立的闭包,而且每个闭包都分别拥有对参数的引用。由于这些显而易见的因素,闭包确实非常用以带来泄漏。下面的示例将展示使用闭包的主要泄漏因素:

<html>
<head>
<script language="JScript">

function AttachEvents(element)
{
 // This structure causes element to ref ClickEventHandler
 element.attachEvent("onclick", ClickEventHandler);

 function ClickEventHandler()
 {
  // This closure refs element
 }
}

function SetupLeak()
{
 // The leak happens all at once
 AttachEvents(document.getElementById("LeakedDiv"));
}

function BreakLeak()
{}
</script>
</head>
<body onload="SetupLeak()" onunload="BreakLeak()">
<div id="LeakedDiv"></div>
</body>
</html>

  如果你对怎么避免这类泄漏感到疑惑,我将告诉你处理它并不像处理普通循环引用那么简单。"闭包"被看作函数作用域中的一个临时对象。一旦函数执行退出,你将失去对闭包本身的引用,那么你将怎样去调用detachEvent方法来清除引用呢?在Scott Isaacs的MSN Spaces上有一种解决这个问题的有趣方法。这个方法使用一个额外的引用(原文叫second closure,可是这个示例里致始致终只有一个closure)协助window对象执行onUnload事件,由于这个额外的引用和闭包的引用存在于同一个对象域中,于是我们可以借助它来释放事件引用,从而完成引用移除。为了简单起见我们将闭包的引用暂存在一个expando属性中,下面的示例将向你演示释放事件引用和清除expando属性。

<html>
<head>
<script language="JScript">

function AttachEvents(element)
{
 // In order to remove this we need to put
 // it somewhere. Creates another ref
 element.expandoClick = ClickEventHandler;

 // This structure causes element to ref ClickEventHandler
 element.attachEvent("onclick", element.expandoClick);

 function ClickEventHandler()
 {
  // This closure refs element
 }
}

function SetupLeak()
{
 // The leak happens all at once
 AttachEvents(document.getElementById("LeakedDiv"));
}

function BreakLeak()
{
 document.getElementById("LeakedDiv").detachEvent("onclick",
 document.getElementById("LeakedDiv").expandoClick);
 document.getElementById("LeakedDiv").expandoClick = null;
}
</script>
</head>
<body onload="SetupLeak()" onunload="BreakLeak()">
<div id="LeakedDiv"></div>
</body>
</html>

  在这篇文章中,实际上建议我们除非迫不得已尽量不要创建使用闭包。文章中的示例,给我们演示了非闭包的事件引用方式,即把闭包函数放到页面的全局作用域中。当闭包函数成为普通函数后,它将不再继承其父函数的参数和局部变量,所以我们也就不用担心基于闭包的循环引用了。在非必要的时候不使用闭包这样的编程方式可以尽量使我们的代码避免这样的问题。

  最后,脚本引擎开发组的Eric Lippert,给我们带来了一篇关于闭包使用通俗易懂的好文章。他的最终建议也是希望在真正必要的时候才使用闭包函数。虽然他的文章没有提及闭包会使用的真正场景,但是这儿已有的大量示例非常有助于大家起步。

  不得不说Eric Lippert同志疾呼的:Don't use closures unless you really need closure semantics. In most cases, non-nested functions are the right way to go. 是非常消极的应付之辞。今天关于IE内存泄漏的文章已有很多,而且很大部分就是微软的自己人在解释,甚至象Eric Lippert这样引擎开发组的成员。但是他们的文章始终没有正面承认其实这就是IE的bug,而且是非常严重的bug,这事情其实完全不关脚本引擎对象和DOM对象的事。就是微软对产品不负责任的表现,不说IE4,1997那个春天那是太遥远了点,但是IE6也是2001年随xp发布的。使用COM可以给他们的开发带来很多便利,当然也利用很多现成的东西,可是居然在带来这样的严重问题后,他们却把大部分责任归咎于不合理和不正确的使用Closures技术!对于循环引用产生Memory Leak我根本就是不好意思提了,那样的问题是应该发生的吗?那就让我们拭目以待看IE7怎么解决这堆shit问题吧,当然我希望不要再看到类似:Do not use closures unless they are necessary. 这样的扯淡建议!
最新更新
·如何使用GoDaddy免费域名解析服务
·Google Voice呼转到中国电话的五种方法
·国内免费网盘横向评测,体验与分析
·解决Gmail、Google Reader无法登录的问
·教你在Windows7下安装支付宝控件和证书
·快速获取电脑报论坛邀请码的方法
·关于搜索和下载国外资源的一点心得
·GoDaddy Coupons(GoDaddy优惠码)大全
·解决QQ可以上却不能上网(打不开网页)
·大陆和台湾电脑网络用语对照表
相关信息
·XNview 1.97中文版下载-替代AcdSee的免费看图软件
·如何用IE8浏览器使用多个用户登录一个网站?
·TuneUp Utilities 2010下载(注册码)-PC的瑞士军刀
·Web浏览器15年:花落谁家未可知
·SysFader:IEXPLORE.EXE错误完美解决方法
·谷歌浏览器Google Chrome 3.0正式版下载
·在IE7下生成高质量CSS缩略图
·Windows Live Movie Maker 1.0简体中文版下载
·彻底解决SysFader:IEXPLORE.EXE应用程序错误
·如何正确删除IE8浏览器正式版?
 画心
 愚爱
 偏爱
 火苗
 白狐
 画沙
 犯错
 歌曲
 趁我
 稻香
 小酒窝
 狮子座
 小情歌
 全是爱
 棉花糖
 我知道
 钻石糖
 Nobody
 我爱他
 套马杆
 星空物语
 最后一次
 少女时代
 穿越人海
 断桥残雪
 美了美了
 明天过后
 我很快乐
 到了明天
 心痛2009
 爱丫爱丫
 寂寞好了
 敢不敢爱
 郎的诱惑
 爱情买卖
 super girl
 我叫小沈阳
 i miss you
 姑娘我爱你
 我们都一样
 其实很寂寞
 习惯了寂寞
 我要的飞翔
 我的好兄弟
 爱的华尔兹
 李雷和韩梅梅
 贝多芬的悲伤
 爱我就跟我走
 丢了幸福的猪
 我只是个传说
 要嫁就嫁灰太狼
 即使知道要见面
 如果我变成回忆
 看得最远的地方
 斯琴高丽的伤心
 别在我离开之前离开
 不是因为寂寞才想你
 爱上你等于爱上了错
 在心里从此永远有个你
 一个人的寂寞两个人的错