zoukankan      html  css  js  c++  java
  • innerHTML引起IE的内存泄漏

     

     

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

    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也不现实。。。

  • 相关阅读:
    mysql数据库中的锁
    HihoCoder
    旅游规划(双权连通图)
    单调栈的原理
    战争联盟(并查集)
    点赞狂魔(巧用STL容器事半功倍)
    这是二叉搜索树吗?
    好像是两种权的dijkstra
    pat--046.整除光棍(除法模拟)
    幸运数字 2
  • 原文地址:https://www.cnblogs.com/firstdream/p/5533134.html
Copyright © 2011-2022 走看看