内存泄漏常见的原因有三种:

1. 闭包

2. 未解除事件绑定

3. 循环引用DOM元素

除此之外,还有一种泄漏原因少有人知,它和innerHTML有关,不过很容易解决。

出现这种内存泄漏需要有三个条件:

  1. 内存中存在一个未加入DOM树的元素

  2. 给这个元素设置innerHTML,注意,必须是能创建元素并且绑定了DOM 0级事件

  3. 必须在这个元素加入DOM树前设置它的innerHTML

举个例子:

// 创建一个仅存在于内存中的元素
var el = document.createElement('div');
// 设置innerHTML
el.innerHTML = '<a onclick = "testFn()">Test Link</a>';
// 加入DOM树
document.body.appendChild(el)

这种写法很常见对吧,但你根本察觉不到有内存泄漏。唯一的隐患在于,当你在一个相同的页面上频繁地用这种方式设置innerHTML,一次又一次,反反复复,没完没了,好吧,其实也没那么多次,总之是很多次之后,就会出现问题了。

肯定有人会说,谁那么蛋疼地总折腾一个元素,其实在ajax泛滥的时代,经常需要动态更新页面,所以这种情况也并非罕见。

如果实在不信,这里有两个DEMO页面:

泄漏DEMO

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head> <title>IE innerHTML Memory Leak Demo</title> <style type = "text/css">
html,body
{
font-family: arial;
font-size: 120%;
} div a
{
font-size: 120%;
display: block;
margin: 5px;
padding: 5px;
border: 2px solid #000;
background-color: lightgreen;
}
</style> <script type = "text/javascript"> var btnStart, btnStop; function init()
{
btnStart = document.getElementById('btnStart');
btnStop = document.getElementById('btnStop');
btnStart.onclick = startLeak;
btnStop.onclick = stopLeak;
} function startLeak()
{
btnStart.disabled = true;
btnStop.disabled = false;
leak();
} function stopLeak()
{
btnStop.disabled = true;
btnStart.disabled = false;
} function leak()
{
if (btnStop.disabled == true)
{
return;
} var str = '';
var i, len = 2000;
for (i = 0; i < len; i++)
{
str += '<a onclick = "test()">Test Link</a>';
} var elem = document.getElementById('testDiv');
if (elem) document.body.removeChild(elem); var elem = document.createElement('div');
elem.id = 'testDiv'; // Oops! Setting .innerHTML first, and _then_ calling .appendChild(..) is asking for a memory leak!
elem.innerHTML = str;
document.body.appendChild(elem); setTimeout(leak, 250);
} function test()
{
alert('Click!');
return false;
} window.onload = init;
</script>
</head> <body>
<h1>IE innerHTML Memory Leak Demo</h1> <p>Upon clicking the "Start Leak" button, a script will execute repeatedly which creates a new <div> element in memory,
sets its innerHTML to a string of 2000 <a> tags with onclick events wired up ('<a onclick = "test()">Test Link</a>'),
and then adds that <div> to the
page.</p> <p>Letting this script run for about 60 seconds, and using Perfmon to monitor memory consumption, you should notice a significant
increase in the amount of memory consumed. To see the same script logic that doesn't leak memory, view the
<a href = "./noleak.html">No Leak Page</a>.</p> <button id = "btnStart">Start Leak</button>
<button id = "btnStop" disabled = "disabled">Stop Leak</button>
</body>
</html>

  

不泄露DEMO

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head> <title>IE innerHTML Memory Leak Demo (the fix)</title> <style type = "text/css">
html,body
{
font-family: arial;
font-size: 120%;
} div a
{
font-size: 120%;
display: block;
margin: 5px;
padding: 5px;
border: 2px solid #000;
background-color: lightgreen;
}
</style> <script type = "text/javascript"> var btnStart, btnStop; function init()
{
btnStart = document.getElementById('btnStart');
btnStop = document.getElementById('btnStop');
btnStart.onclick = startLeak;
btnStop.onclick = stopLeak;
} function startLeak()
{
btnStart.disabled = true;
btnStop.disabled = false;
leak();
} function stopLeak()
{
btnStop.disabled = true;
btnStart.disabled = false;
} function leak()
{
if (btnStop.disabled == true)
{
return;
} var str = '';
var i, len = 2000;
for (i = 0; i < len; i++)
{
str += '<a onclick = "test()">Test Link</a>';
} var elem = document.getElementById('testDiv');
if (elem) document.body.removeChild(elem); var elem = document.createElement('div');
elem.id = 'testDiv'; // Add the element to the DOM first, and /then/ set .innerHTML to prevent memory from leaking.
document.body.appendChild(elem);
elem.innerHTML = str; setTimeout(leak, 250);
} function test()
{
alert('Click!');
return false;
} window.onload = init;
</script>
</head> <body>
<h1>IE innerHTML Memory Leak Demo (the fix)</h1> <p>Upon clicking the "Start Leak" button, a script will execute repeatedly which creates a new <div> element in memory and
then adds that element to the page. Only <em>after</em> the element has been added to the page, do we set its .innerHTML to a
string to 2000 <a> tags with onclick events wired up ('<a onclick = "test()">Test Link</a>').</p> <p>Letting this script run for about 60 seconds, and using Perfmon to monitor memory consumption, you should notice that,
unlike the <a href = "./leak.html">Leak Page</a>, memory consumption remains relatively constant.</p> <button id = "btnStart">Start Leak</button>
<button id = "btnStop" disabled = "disabled">Stop Leak</button>
</body>
</html>

  

接着来看怎么解决它:

其实很简单,换个顺序,先把元素加入DOM树,再设置innerHTML。

当然你也可以完全放弃使用innerHTML,这样做好处多多,比如不会存在未解除事件绑定的情况,但貌似完全放弃innerHTML也不现实。。。

最新文章

  1. 如何手动安装MySql
  2. request 对象和 response 对象
  3. vue.js学习之入门实例
  4. Linux - 升级+编译kernel
  5. HDU5758 Explorer Bo 树形dp
  6. php-fpm 启动不了 libiconv.so.2找不到
  7. BZOJ_1013_[JSOI2008]_球形空间产生器_(高斯消元)
  8. ACM——回文
  9. linux下建立无线wifi------简单实用!
  10. Rss web 工具 大对比
  11. hprof教程
  12. Objective-C中math.h数学计算公式介绍
  13. 面向对象UML中类关系
  14. 配置 Gitblit 进行 Git 代码管理
  15. PuTsangTo-单撸游戏开发04 给角色添加基本动画
  16. IPhoneX网页布局
  17. 4sumii
  18. Python之--paramiko实例
  19. SkylineGlobe7.0.1版本 主页面如何和Popup里面的嵌入页面相互传值
  20. 给力的移动 FZU - 2287

热门文章

  1. sql 简单查询修改
  2. 通过Toad工具查看dmp里面的表
  3. Java 给Thread传递参数
  4. fastText入门
  5. 编写hadoop程序并打成jar包上传到hadoop集群运行
  6. Python学习之旅—生成器与迭代器案例剖析
  7. CodeForces 718A Efim and Strange Grade (贪心)
  8. UVALive 6833【模拟】
  9. 51nod 1004 【快速幂】
  10. bzoj 3027: [Ceoi2004]Sweet【生成函数+组合数学】