`

JavaScript优化小记

 
阅读更多

前段时间接到一个任务:对项目组的Web项目主画面进行性能优化。

画面情况是:监控设备数量200多台,画面每10秒钟查询并更新数据,如果不对比缓存每10s进行更新,那么在IE浏览器下15分钟不到增长了88MB(用F12内存工具查看),而且会一直增长,直至浏览器崩溃...

以JS生成的html代码主要结构如下(填入HTML上的DIV标签内,$('').html(...)):

 

<ul >
<li><a><img /></a><div></div></li>
<li><a><img /></a><div></div></li>
...
<li><a><img /></a><div></div></li>
<ul>



网上找了sIEve工具对web页面进行了分析,

结论是:

1,每10秒钟内存会增加1MB多但是释放的不过十几KB,js循环中大量DOM节点建立(UL,内部包括LI/DIV/A节点),但是从未释放,以至内存占用曲线就是上升直线,DOM对象曲线中观察DOM数量也是不停增加。最主要的是js中建立的UL元素始终是orphan(孤立)节点。

2,每10秒钟为画面中的一组checkbox绑定一次jquery.ui.button操作,每次都生成了很多DIV /SPANdom对象,都是孤立节点,占用的内存始终没被浏览器释放;

于是,针对结论1写了个函数准备每次重画UL列表前主动清除内部节点包括UL标签本身:

 

	var releasememory3 = function(obj){		

		$(obj).find("a").unbind();
		$(obj).find("li").unbind();
		$("#rightmenu ul > li").unbind();

		var li0 = $(obj).find("li").eq(0);		
		removeLIAllsibling(li0);
		li0 = null;			
	}
	var removeLIAllsibling = function(_elem){//
		var _parentElement = _elem.parentNode;
		if(_parentElement){
			
			for (var i=0;i<_parentElement.childNodes.length;i++){
				 var childNode = _parentElement.childNodes[0];  // li
				                            
				 var div0 = childNode.getElementsByTagName("DIV");//div
				 for(var j=0;j<div0.length;j++){//div
					 div0[j].innerHTML = '';
					 childNode.removeChild(div0[0]);
				 }
				 div0= null;
				 
				 var a0 =  childNode.getElementsByTagName("A");//A
				 for(var k=0;k<a0.length;k++){//A					 
					 var img0 = a0[k].getElementsByTagName("IMG");//IMG
					 for(var m=0;m<img0.length;m++){
						 a0[k].removeChild(img0[0]);
					 }
					 img0 = null;
					 a0[0].onclick = null;
					 childNode.removeChild(a0[0]);
				 }
				 a0 = null;				 	
				 childNode.onclick = null;
				 _parentElement.removeChild(childNode);
			}
      	}
		_parentElement = null;
	};


测试结果是此releasememory3函数成功清除了孤立节点IMG/DIV/A/LI,但是无法清除UL节点。

 

 

由于js建立的UL标签始终是孤立节点,故把ul放在JSP的html部分,在js中仅仅生成li节点;

再次测试清除,IMG/DIV/A/LI都可以释放;

从sIEve测试结果看这样改写后的LI/A/DIV/IMG节点都不再是孤立节点每次更新HTML DIV中的ul内部节点都可以被浏览器释放。结论1的问题解决。

 

针对结论2,由于循环不断给html画面上的input控件绑定juery.ui.button操作,查看jquery.ui.button.js库文件,确实每次都会建立span对象并绑定很多事件,这部分不属于画面代码,比较难以主动清除,所以,解决办法是只作一次jquery.ui.button的绑定操作,循环中只更新input label标签中的变化内容text属性。问题2解决。

 

比较明显的2个内存泄露问题解决。

 

列举问题可能的原因:

 

1, Js代码DOM孤立节点、

2, js全局变量滞留内存、

3, js代码DOM对象注册匿名函数闭包导致的循环引用、

4, js对象dom对象相互引用导致内存不释放、

5,一般 js闭包导致的循环引用导致内存不释放、

6, js隐式对象转换导致的对象创建不释放、

7, js中DOM节点增加顺序错误导致内存不释放、

8, js大量循环重复重写同一对象属性(新建很多对象不释放)

9, DOM对象绑定事件未清除(可能在引用库比较多)

10,卸载页面时直接解绑事件、删除全局变量(无法解决页面不跳转时的内存增长,解决画面切换时的内存增长)

 

引用其他经验者的一段启发性文字:

 

 IE下的内存泄露原因就是循环引用,IE的垃圾回收器不能很好处理这种引用。 
会产生泄露的循环引用,只有孤立的DOM对象(脱离DOM树)。 
孤立的DOM对象间的循环引用,孤立的DOM对象与JS对象的循环引用。 
为什么是孤立的DOM对象呢?在离开页面时(刷新,跳转)会删除整个DOM树,在DOM树上的对象也会被删除,就算有循环引用,此时被打断。 
孤立的DOM对象有:一、用JS创建但未加入DOM树。二、从DOM树中删除的。 
所以避免泄露,就尽量不要让这样的DOM对象产生。 
有时候这样对象又不可避免,那就不要让这样对像产生循环引用。 
最常见的循环引用是由闭包产生,其执行环境中的变量(包括参数)引用了DOM对象。



 

 

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics