找出并解决 JavaScript 和 Dojo 引起的浏览器内存泄露问题
简介
一般来说,浏览器的内存泄漏对于 web 应用程序来说并不是什么问题。用户在页面之间切换,每个页面切换都会引起浏览器刷新。即使页面上有内存泄漏,在页面切换后泄漏就解除了。由于泄漏的范围比较小,因此常常被忽视。
Ajax 技术引入后,内存泄漏就成了一个比较严重的问题。在 web 2.0 样式页面上,用户不需要经常刷新页面。Ajax 技术用于异步更新页面内容。特殊场景中,整个 web 应用程序构建在一个页面上。在这种情况下泄漏会被累积,不能忽略。
在本文中,了解内存泄漏是怎样发生的,以及如何通过 sIEve 找到泄漏的源头。这些问题和解决方案的的实际示例可以帮助您探究问题。您可以?下载?本文示例源代码。
使用 JavaScript 和 Dojo 工具包的经验有助于您理解这篇文章,但并不是必需的。
?
泄漏模式
如 web 开发人员所知道的,IE 不同于 Firefox 和其他的浏览器。本文所讨论的内存泄漏模式和问题主要是针对 IE 浏览器的,但不限于 IE。好的方法应该是适用于所有的浏览器的。
IE 怎样管理内存的话题不在本文范围内,参考资料?中有更多信息。由于 JavaScript 的本质和 JavaScript 和 DOM 对象的浏览器内存管理,JavaScript 编码不慎导致了浏览器的内存泄露。造成了这些泄露的有两种常见的模式。
清单 1. 循环引用引起的泄露
??
sIEve 简介
sIEve 是一个帮助检测内存泄露的工具。您可以从?参考资料?中下载 sIEve 和访问文档。主 sIEve 窗口如?图 2?所示。
图 2. sIEve 主窗口?
单击?Show in use?时,这个工具非常有用的。您将看到使用的所有 DOM 节点,包括孤立节点和 DOM 节点增加或减少的引用。
图 3?是一个样例视图。泄露的原因如下:
- 孤立节点,在 Orphan 这一列被标记为 “YES” 。
- 对 DOM 节点增加的不正确引用,显示蓝色。
使用 sIEve 找到泄露节点并查看修复它们的代码。
图 3. sIEve:使用的 DOM 节点
?使用 sIEve 找到泄露节点
通过以下步骤检测泄露节点。
- 通过您的 web 应用程序的 URL 启动 sIEve。
- 单击?Scan Now?寻找当前文档中使用的所有 DOM 节点(可选)。
- 单击?Show in use?查看所有 DOM 节点。在这里,所有节点将以红色标识(新条目),因为您刚刚开始。
- 使用 web 应用程序的一些功能,测试是否有泄漏。
- 单击?Scan Now?刷新使用的 DOM 节点(可选)。
- 单击?Show in use。现在,视图中含有一些有趣的信息。可在此找到孤立节点,或者对某个 DOM 节点的异常引用不断增加。
- 分析报告并检查您的代码。
- 必要时,重复步骤 4-8。
sIEve 不能找出您的应用程序中的所有泄露,但是它能找出由子节点造成的泄露。其他的一些信息,例如 ID 和 outerHTML 可以帮助您指出泄露节点。查看控制泄露节点的代码并相应的作出修改。
回页首
应用示例
这一部分包含更多示可引起内存泄露的示例。这些样例以及最佳实践虽然是基于 Dojo 工具包的,但是大多数示例在普通 JavaScript 编程中是有效的。
虽然有很多方法进行清理,但最常见的方法是删除 DOM 以及 JavaScript 对象以避免内存泄露。本节的其余部分将建立在之前介绍过的模式上。
下面的示例包括一个您可以创建的网站。您还可以从网页中删除网络小部件。这些操作将在一个页面上执行,而页面不会刷新。?清单 3?展示了在 Dojo 类中定义的小部件,这个小部件将在后面文章中频繁出现。
清单 3. MyWidget 类使用 dojo.destroy() 或 dojo.empty()
乍看之下,这个问题似乎并不重要。小部件被创建并存储在数组中。它们从数组中弹出,并删除。DOM 节点也脱离了文档 。但是如果用 sIEve 追踪?
create widget?和?remove widget?操作之间的不同,您会发现每次小部件节点都变成一个孤立节点,它会带来内存泄露。图 4?两次展示了创建和删除小部件的示例。
图 4. 小部件节点的泄露
?这种情形可能是一个 IE bug。即使您创建了一个元素并将它附加到文档,然后立即使用?
parentNode.removeChild()?删除。孤立节点仍然存在。您可以使用?
dojo.destroy()?或?dojo.empty()?来清理 DOM 节点。Dojo 执行?dojo.destroy(<domNode>)?来删除在其他地方已经删除的节点,然后销毁它们。Dojo 还将创建一个节点收集这种垃圾。这样您想删除的节点就删除了。(查看 Dojo 源代码获取实现细节。)清单 5?展示了修复该问题的方法。
Using 清单 5. 使用?dojo.destroy()?来删除 DOM 节点使 JavaScript 对 DOM 节点的引用无效
进行清理时,使 JavaScript 对 DOM 节点的引用无效是一个很好的方法。在?清单 3?中,
destroy?方法不能使 JavaScript 对 DOM 节点(this.domNode, this.container)的引用无效。多数情况下,这种情形不会导致内存泄露,但当您在更加复杂的应用程序中工作时,其它对象可能引用您的小部件,这时可能会出现问题。假设您不了解的其他库可是可用的,保持对您小部件的引用,而且由于某些原因,它不能被清除。删除小部件将导致引用的 DOM 节点成为孤立节点。清单 6?显示了更改。
清单 6. 网站的 HTML:添加更多对象 (widgetRepo)来容纳小部件?
解决方案就是在清理过程中使 DOM 节点引用无效,如?清单 7?所示。可能时添加一些无效语句可能是一个好方法,因为这不会影响原始功能。
清单 7. 使 DOM 引用无效断开事件以及取消主题订阅
使用 Dojo,另一个避免内存泄露的方法就是断开您连接的事件并取消您订阅的主题。?清单 8?展示了一个连接及断开事件的例子
使用 JavaScript 编程,通常建议在从文档中删除 DOM 节点之前先断开事件。使用下述的 API 在不同的浏览器上连接及断开事件。
- 对于 IE:
attachEvent?和?detachEvent - 对于其他浏览器:
addEventListener?和?removeEventListener
清单 8. Dojo.connect and dojo.disconnect?设置 innerHTML
如果您在如何使用 JavaScript 设置?
innerHTML?方面不细心的话,可能会引起 IE 内存泄露。(查看?参考资料?获取详情。)?清单 9?展示了可能引起 IE 内存泄露的场景。
清单 9. IE 上的 innerHTML 泄露?
结束语
识别导致浏览器内存泄露的模式很容易,而在您应用程序源代码寻找问题的根源就比较困难。sIEve 能够帮助您找到大多数由孤立节点引起的泄露。本文介绍了,在 JavaScript 编码中仅仅一点微小的疏忽就会引起内存泄漏。本文中介绍的最佳实践可以帮助您防止发生泄漏 。
?
下载
描述名字大小下载方法本文源代码MyWidget.zip1KBHTTP
?
?
?