问题起源

QQ客户端“我的钱包”重复打开/关闭时发现内存泄露。

排查问题

因为之前有过类似的解决经验,我首先快速用下面两种方法对代码进行了调整:

  1. 走查代码,把对于有引用的对象在调用完后主动释放。
  2. 添加onunload方法,将相关的对象销毁。

但是很遗憾,情况并没有好转,内存泄露问题依然存在。

于是,借助工具作进一步分析。

首先,通过IEJSLeaksDetector工具在IE下查看泄露点,发现在用这个工具无法检测出内存泄露的问题。

继续通过FF的内存泄露检测工具的插件Leak Monitor来做进一步检测,基本锁定内存泄露点是在ajax框架中的onreadystatechange属性中。请看下面的截图:

再通过IE8的开发者工具中的探查器可以看到页面上相关的调用树和调用计数,当发送一个ajax请求时,onreadystatechange将会被引用3次

OK,问题终于定位到了。

解决问题

根据之前经验,将xhr.onreadystatechange置为null,但检测之后发现问题仍然存在,继续努力。

采用的setInterval方法来替代onreadystatechange属性(这种方法是之前在YUI那边看到的,呵呵),对代码进行调整如下:

测试,内存波动频率接进正常状态。

结论:Ajax中的onreadystatechange是导致内存泄露主要原因。

小结

代码编写需注意:

  1. 对象使用完后,主动释放资源,将对象置为null。
  2. 尽可能避免闭包使用。
  3. 动态绑定事件后及时移除。
  4. 对于try…cache操作时,增加 finally 块来释放资源(异步处理的时候看情况使用)。
  5. 视情况添加onunload事件销毁对象,将其资源释放。

几类内存泄露情况的参考解决方案:

  1. 循环引用:每次调用完后将其主动释放或放在onunload中释放即可。
  2. 事件绑定:处理完后将其事件移除即可。
  3. 闭包:避免内部循环引用,尽可能少用闭包。
  4. COM/ActiveX组件调用:使用完后将其置为null,特别是给组件加事件的时候需要谨慎处理。

最后介绍一下本次用到的几款小工具,有兴趣的TX可以参考,呵呵

IEJSLeaksDetector

IE > 查看 > 浏览器栏 > js memory leaks detector

修改设置:

泄露点:

IE JSLeaksDetector2

URL: http://blogs.msdn.com/gpde/pages/javascript-memory-leak-detector_v2.aspx

IE8自带的开发者工具

打开IE按F12或工具 > 开发者工具 打开操作界面

点击“开始配置文件”。之后访问你需要的网页或执行页面上的相关操作。完成之后,

点击“停止配置文件”即可看到调用的函数。

参考资源

JavaScript 中的内存泄露模式

http://www.ibm.com/developerworks/cn/web/wa-memleak/index.html
Memory Leaks in Microsoft Internet Explorer
http://isaacschlueter.com/2006/10/msie-memory-leaks/
Re-inventing XMLHttpRequest: Cross-browser implementation with sniffing capabilities
http://www.ilinsky.com/articles/XMLHttpRequest/#bugs-ie-leak