UpdatePanel 中的 jQuery 内存泄漏






4.74/5 (21投票s)
解决 UpdatePanel 中的 jQuery 内存泄漏问题。
引言
本文讨论了如何将 jQuery
库与 Microsoft AJAX.NET 集成,特别是与 <asp:UpdatePanel/>
集成,以及如何避免 Internet Explorer 在此过程中出现内存泄漏。
jQuery
变得越来越流行。然而,Microsoft ASP.NET 和 AJAX.NET 提供了它们自己的与服务器通信的框架。在 ASP.NET 环境中,jQuery
AJAX 机制看起来笨拙且不自然。使用 ASP.NET UpdatePanels
进行 AJAX 功能,并使用 jQuery
进行其 selectors
功能要容易得多——这正是我想象中 Microsoft 计划要做的事情。然而,这样做很棘手,本文解释了这样做的一些不足之处。
迭代 1 - 示例页面
让我们想象一个示例计算器页面,用于将两个数字相加
<body>
<form id="form1" runat="server">
<asp:ScriptManager runat="server" ScriptMode=Debug>
<Scripts>
<asp:ScriptReference Path="~/jquery-1.2.6.debug.js" />
</Scripts>
</asp:ScriptManager>
<asp:UpdatePanel ID='up' runat="server"><ContentTemplate>
<asp:TextBox ID='i1' runat="server" CssClass='num' Width='50px'/>+
<asp:TextBox ID='i2' runat="server" CssClass='num' Width='50px'/>=
<asp:TextBox ID='res' runat="server" Width='50px'/>
<asp:Button ID='btn' runat="server" Text='...'/>
</ContentTemplate></asp:UpdatePanel>
</form>
</body>
<script>
function add() {
$get('res').value = parseInt($get('i1').value)
+ parseInt($get('i2').value);
}
$(document).ready(function() {
$('.num').change(add);
});
</script>
如果您熟悉 ASP.NET 和 jQuery
,则此页面不应需要太多解释
- 我们包含
ScriptManager
和jquery
脚本。 - 标准表单
- 我们定义了 3 个输入控件,前两个输入控件的
class='num'
- 因此我们可以使用jQuery
轻松找到它们 - 内容 放置在
<asp:UpdatePanel>
中,并提供一个按钮来刷新面板 - 定义了一个函数,用于获取 2 个数字,将它们相加,并将结果存储在第三个输入框中
- 提供了标准的
jQuery
机制来附加 change 事件,因此当其中一个数字发生更改时,将执行计算 。
生成的页面如下所示

最初,这些东西似乎有效。您可以更改数字,并在屏幕上看到结果。但是,尝试刷新 UpdatePanel
- 然后突然计算中断。
解释
UpdatePanel
刷新后失败的原因是 UpdatePanel
的内容以及附加到它们的所有事件都消失了。
要解决此问题,我们需要使用 AJAX.NET 提供的将事件附加到页面的方法 (这里我只附加了脚本部分)
function add() {
$get('res').value =
parseInt($get('i1').value) + parseInt($get('i2').value);
}
Sys.Application.add_load(function() {
$('.num').change(add);
});
迭代 2 - 内存泄漏
迭代 1 中建议的方法对我的作用持续了一段时间。除了最终,我开始注意到 Internet Explorer 变慢了。非常慢。然后,查看进程监视器,我注意到 Internet Explorer 会在类似兆字节的 UpdatePanel
刷新中泄漏内存。查看 sIEve 工具(感谢创建者),我发现输入元素没有被销毁

您可以看到,每次单击刷新时,它都会创建一个新的输入元素集,而没有销毁之前的元素。
解释
在 Google 上搜索一段时间后,我注意到 Internet Explorer 内存泄漏是许多开发人员的痛点。大多数投诉被确定为伪泄漏(内存实际上被释放,但您需要完全刷新页面才能看到它)或作为 closures。
closure 是一个有趣的话题。它指的是附加到 DOM 元素的 JavaScript 事件。显然,Internet Explorer 使用 2 个垃圾回收器 - 一个用于 DOM,另一个用于 JavaScript。其中涉及一些语义,但底线是,如果您将事件附加到 DOM 对象,则任何一个垃圾回收器都无法摆脱涉及的对象/DOM 元素。我强烈建议您在 WEB 上阅读更多关于闭包的信息。
好吧,jQuery
就是关于闭包的。它们很好地附加到 window.unload
并在页面卸载时进行清理,但它们不知道 UpdatePanel
。当 UpdatePanel
即将刷新时,需要做些事情来清理 jQuery
。
迭代 3 - jQuery 清理插件
UpdatePanel
确实提供了清理钩子。当它即将删除一个元素时,它会检查是否存在 element.dispose
函数。如果存在这样的函数,则它将被执行。
所以我最终创建了以下 jQuery
插件来处理这种情况
(function($) {
$.fn.Disposable = function(cln) {
return this.each(function() {
var el = this;
if (!el.dispose) {
el.dispose = cleanup; // will be called by
// Microsoft for cleanup
$(window).bind("unload", cleanup);
}
function cleanup() {
if (!el)
return;
$(el).unbind();
$(window).unbind("unload", cleanup);
el.dispose = null;
el = null;
};
});
};
})(jQuery);
function add() {
$get('res').value =
parseInt($get('i1').value) + parseInt($get('i2').value);
}
Sys.Application.add_load(function() {
$('.num').change(add).Disposable();
});
如您所见,此 jQuery
插件将 cleanup()
函数附加到 element.dispose
。每当调用 element.dispose
时,所有 jQuery
事件都将从该元素中取消绑定,并允许垃圾回收器回收所有对象。
总结
我正在讨论的代码实际上是我之前在 基于 jQuery 的 Ajax.Net 库 中发布的另一个库的一部分。欢迎您下载该项目并在闲暇时浏览代码。
历史
- 2009 年 3 月 20 日:初始发布