`
zTreeAPI
  • 浏览: 345648 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

IE9关联数组导致内存泄漏测试报告

阅读更多

 

最近为了满足一部分朋友的需求,给 zTree 提供了 destroy 的方法,用于让 zTree 自行清空。为了检查该方法是否有效,做了一个简单的测试——显示5000个节点然后清空,此操作循环100次,结果发现 IE9 下内存严重暴涨,于是进行了反复筛查,最终锁定了嫌疑犯:关联数组(data[key] = value)导致的内存泄漏!

只找到了嫌疑犯不行,定罪要有证据的,设计了一个简单的模型专门进行这个情况的测试,不排除可能由于我的疏忽得到的错误的结论,因此非常欢迎大家踊跃发表自己的看法,随便喷吧。。。。

【测试模型】

不要DOM和闭包、匿名函数干扰,一个全局变量 data 用于保存生成的数据;一个全局变量max用于设置数据最大条数;一个Function用于创建数据;一个Function 用于销毁数据。两个按钮,分别用于触发这两个Function。

补充:为了进行多种情况对比,因此一共制作了8组创建数据、销毁数据的 Function ,详细见后面的说明。

【测试流程】

 

 

【测试环境】

Win7 64位操作系统;IE Tester下的 IE6、8、9;FireFox v14.0.1;Chrome v21.0.1180.79;IE6性能太差,因此设置数据条数 max=100000;其他的浏览器都设置 max=1000000;

【开始测试】

1) 第一组测试:

创建对象:关联数组的 key 值无限递增,绝不重复

销毁对象:递归遍历对象内部属性,逐一清空

var data = new Object();
var i=0,j, max=1000000;

function initJson_1(){
	for (j=i+max; i<j; i++) {
		data["_" + i] = {id:i, name:"name" + i};
	}
}

function clearJson_1(){
	clearItem(data);
}

function clearItem(jsonObj) {
	for (var s in jsonObj) {
		var obj = jsonObj[s];
		if (obj instanceof Array) {
			for(var i=obj.length-1; i>=0; i--) {
				clearItem(obj[i]);
				obj.pop();
			}
		}else if (obj instanceof Object){
			clearItem(obj)
		}else{
		}
		jsonObj[s] = null;
		delete jsonObj[s];
	}
	jsonObj = null;
}

 测试结果: 

 

 

测试分析:

这种递归逐一清空对象属性的方法对于 IE6 和 Chrome 有一定效果,FireFox杯具了直接无响应(缩小max值后可以正常);IE8内存暴涨,貌似与这个清空的方法有关,第三次之后再执行方法就无反应了;IE9内存稳步上升,即使最后刷新页面也仍然占有大量内存。

2) 第二组测试:

创建对象:关联数组的 key 值无限递增,绝不重复

销毁对象:直接将 data 设置为 new Object()

var data = new Object();
var i=0, max=1000000;

function initJson_2(){
	for (j=i+max; i<j; i++) {
		data["_" + i] = {id:i, name:"name" + i};
	}
}

function clearJson_2(){
	data = null;
	data = new Object();
}

 测试结果:

 

测试分析:

与第一组对比,很明显清空对象直接设置为 new Object 即可,完全没有必要自己递归逐一清空,那样只会起到反作用(明显降低效率)。这次的清空操作并不会立刻让垃圾回收机制工作,但对于非IE9 的浏览器都能在重新创建对象时进行垃圾回收,IE9继续保持内存增长状态。

3) 第三组测试:

创建对象:关联数组的 key 值固定,每次创建都在固定范围内

销毁对象:直接将 data 设置为 new Object()

var data = new Object();
var i=0,j, max=1000000;

function initJson_3(){
	for (i=0; i<max; i++) {	
		data["_" + i] = {id:i, name:"name" + i};
	}
}

function clearJson_3(){
	data = null;
	data = new Object();
}

 测试结果:

测试分析:

因为key值被固定了范围,反复测试都会生成同样的key,所以IE9的内存在增长两次后就不再上涨了。另外这次测试也可以看出FireFox 和 Chrome的垃圾回收机制会在其认为需要的时候自动执行。

4) 第四组测试:

创建对象:关联数组的 key 值无限递增,绝不重复

销毁对象:直接将 data 设置为 {}

 

var data = new Object();
var i=0,j, max=1000000;

function initJson_4(){
	for (j=i+max; i<j; i++) {
		data["_" + i] = {id:i, name:"name" + i};
	}
}

function clearJson_4(){
	data = null;
	data = {};
}

 测试结果:

测试分析:

本组测试主要是与第二组测试进行对照,表明释放对象将其设置为 {} 或 new Object() 是没有什么区别的。

5) 第五组测试:

创建对象:关联数组的 key 值无限递增,绝不重复,同时利用 eval 实现 data.key = value方式进行赋值

销毁对象:直接将 data 设置为 {}

var data = new Object();
var i=0,j, max=1000000;

function initJson_5(){
	for (j=i+max; i<j; i++) {
		eval('data._'+i+'= {id:i, name:"name" + i};');
	}
}

function clearJson_5(){
	data = null;
	data = {};
}

 测试结果:

测试分析:

这次改变只能证明eval 能不用的时候千万别用! 居然把IE9 搞死了;IE8内存很高,但因为无内存泄漏的情况所以没有死掉。而且其他的浏览器在测试时速度也都明显下降。

6) 第六组测试:

创建对象:将 data 设置为 Array,关联数组的 key 值无限递增,绝不重复,且 key 仍为 String

销毁对象:直接将 data 设置为 null

var data = new Object();
var i=0,j, max=1000000;

function initJson_6(){
	data = [];
	for (j=i+max; i<j; i++) {
		data["_" + i] = {id:i, name:"name" + i};
	}
}

function clearJson_6(){
	data = null;
}

 测试结果:

测试分析:

继续与第二组测试进行对比,会发现data 是 Array 还是 Object,如果都用关联数组的方式,仍然用对象的方式使用,测试结果依旧是唯有IE9不断攀升呀。

7) 第七组测试:

创建对象:将 data 设置为 Array,关联数组的 key 值无限递增,绝不重复,且 key 修改为 number,可以直接使用数组模型

销毁对象:直接将 data 设置为 null

var data = new Object();
var i=0,j, max=1000000;

function initJson_7(){
	data = [];
	for (j=i+max; i<j; i++) {
		data[i] = {id:i, name:"name" + i};
	}
}

function clearJson_7(){
	data = null;
}
 

测试结果:

测试分析:

采用了纯数组的方式保存value,及时index不断上涨,仍然不会造成浏览器的内存无限上涨,IE9也正常了。

8) 第八组测试:

创建对象:将 data 设置为 Array,关联数组的 key 值固定,每次创建都在固定范围内,且 key 修改为 number,可以直接使用数组模型

销毁对象:利用 pop() 方法逐一删除数组元素,最后将 data 设置为 null

var data = new Object();
var i=0,j, max=1000000;

function initJson_8(){
	data = [];
	for (i=0; i<max; i++) {	
		data[i] = {id:i, name:"name" + i};
	}
}

function clearJson_8(){
	while(data.length>0) {
		data.pop();
	}
	data = null;
}
 

测试结果:

测试分析:

与第一组测试对比会发现IE8对于用js 逐一清空对象的操作貌似依然有一些性能的损失;IE6最悲催,与第一组测试中的FireFox 一样,在逐一清空对象时倒下了(缩小max值后可以正常)。

【测试总结】

  1. 核心问题,关联数组的操作在IE9下会引起严重的内存泄漏。个人猜测——虽然通过清空data对象的key、value对应,但IE9的内存回收机制依然认为该key-value有关联,并且不会释放空间;所以当key值固定范围后会发现内存上升到一定范围后就停止了。
  2. 清空对象时直接设置为 null、{}、new Object() 就可以了,没必要逐一逐层进行清除,那样只会降低效率。
  3. IE8表现平平,没有突出的作为;IE6整体性能太弱,所以只能降低测试标准。但对于这个关联数组的操作方面没有什么问题,一直都比较稳定。
  4. FireFox仍然只有一个进程控制所有的tab包括其内核,导致测试时每次运行后,都要多等一会儿,看稳定了再记录内存值。并且 FireFox 的内存回收机制貌似有点儿过于频繁,导致了看它的测试结果总是飘飘忽忽的。
  5. Chrome依然优秀,不管是内存占有量、垃圾回收速度、代码运行速度都很好。

【解决建议】

  1. 是不是应该告诉微软修改这个严重的Bug 呢?
  2. 如果能使用Array 就别用这种关联数组了;如果必须使用关联数组,那么尽量避免key值无限增长,至少可以缓解内存泄漏的伤害…
  3. 是否可以考虑自己制作js 的HashMap?这个我还没有时间做测试,但相信应该性能方面会比原生的关联数组慢不少,难道因为IE9 的Bug 就让我们改变整个代码的架构吗???
6
5
分享到:
评论
4 楼 zTreeAPI 2012-12-20  
upflyer 写道
最近也在搞前台浏览器内存的问题,竟然发现有一些ie8可以的页面在ie9下反而不好,看到这个文章非常感谢楼主。
另外,想请教一下楼主,如何得到创建对象与清理对象时的内存使用量,是有工具还是写代码?能否提供一下方法,非常感谢~

都木有,直接用观察任务管理器看的
3 楼 upflyer 2012-12-20  
最近也在搞前台浏览器内存的问题,竟然发现有一些ie8可以的页面在ie9下反而不好,看到这个文章非常感谢楼主。
另外,想请教一下楼主,如何得到创建对象与清理对象时的内存使用量,是有工具还是写代码?能否提供一下方法,非常感谢~
2 楼 zTreeAPI 2012-08-22  
chujing_2010 写道
提点自己建议:...


非常高兴能有个回音,在公司的机器木有IE9,只能晚上在家按照你的建议进行测试,结果如下:

1、测试1中你修改了递归清除属性的代码,递归木有了,所以也就不会死机了(使用递归方法清除内容如果将 max 设置为10000 是不会导致 FF死掉的,涨到 100000 就会死翘翘),但这也就失去了这个测试的目的。
   但我的确疏忽了,在clearJson_1中没有设置 data = null;不过修改后重新测试了一下,测试结果与我之前的几乎一样。

2、关于 clear Function 中设置为 null 后又设置了 new Object() 或 {} 的问题,一方面是时间问题,没有那么细致的处理;另一方面也是因为发现设置了 null 和 new Object() 以及 {} 的效果几乎是一样的,所以在测试代码中也就没有进行特别对待。
   不过我按照你的建议修改后(即每次 start的时候给 data 设置为 new Object() 或 {};clear 的时候设置为 null),测试时并没有发现明显的改善,问题依旧

3、测试6、7 的关键点就在于对比 关联数组 对处理 Array 或 Object 时是否会有不同。
   key 值是字符串或 数字;如果是数字那么就使用了数组的基本功能,如果是 字符串那么应该继续按照 Object 来处理,测试6的结果就表明了 不管是否为 Array,只要按照Object 来处理关联数组的方式,就会有内存泄漏

4、如果不使用关联数组,那么就不能使用变量作为属性值,都要直接用 data.x1 = {...},这个恐怕要单独生成一套拥有上百万行的 js 赋值代码来测试了,等有空时再做进一步的探索吧。毕竟实际应用很难出现这种场景的。

总之,我个人还是认为这是一个 IE9 垃圾回收的 bug,而不是垃圾回收的策略问题。 尤其是 测试2 和 测试3 的对比可以看到 key 值的范围影响了内存泄漏的多少。

另,我将修改后的 测试页面也加入到了 附件中(IE9_leaks v2.zip),可以参考
1 楼 chujing_2010 2012-08-22  
提点自己建议:


测试1:function clearItem(jsonObj)  方法里面释放了data的引用jsonObj的空间 没释放全局变量data的空间。IE 内存都有攀升。firefox 崩溃是for (var s in jsonObj) 我稍作修改firefox 就不崩溃了。
var data = new Object(); 
var i=0,j, max=1000000; 
 
function initJson_1(){ 
    for (j=i+max; i<j; i++) {
        data["_" + i] = {id:"id"+i, name:"name" + i}; 
    } 

 
function clearJson_1(){ 
clearItem(data);  

 
function clearItem(jsonObj) { 
    for (var s in  jsonObj) { 
        var obj = jsonObj[s]; 
        if (obj instanceof Array) {  
            for(var i=obj.length-1; i>=0; i--) { 
                clearItem(obj[i]); 
                obj.pop(); 
            } 
        }else if (obj instanceof Object){
for (key in obj){
obj[key] = null; 
delete obj[key]; 
}
        }else{
jsonObj[s] = null; 
delete jsonObj[s];  
        }  
    } 
    jsonObj = null; 

测试2,测试3 测试4 测试5 你在最后data =null 的同时又申请了一块空间

data = new Object 或者data = {} 都会分配一块空间  怀疑 IE9 data =null 后是因为垃圾释放机制 data 内的小变量没释放总体 设data = null 后 重新申请一块空间。



测试6,7,8

var data = new Object(); 
var i=0,j, max=1000000; 
 
function initJson_6(){ 
    data = []; 

data  = new Object(); 申请空间后,你的引用又 重新 申请了 一个数组空间 使data = [] 指向数组空间。 new Object() 没释放 正确的是 data = null; 再申请 data = [];

chrome 随着应用改变就直接销毁了。



另外数组下标 就是数字。 data["_"+i]  这么用看成data ={} 用  而 (data={} data =[] ) 方法属性还是有差别的。 数组增加有. push() 方法。 感觉你有点混用了。


eval javascript 编程最好避开不用任何浏览器都效率低。


可能按我说修复后就没这么大的差别。是IE9 空间分配和垃圾回收策略。不知道对象不用关联数组形式是否有这样问题。问题留给更多探索者。
   

相关推荐

    06-Java基础(数组-内存图解)

    了解数组和其内存管理机制对于优化代码性能、避免内存泄漏以及理解其他高级概念(如垃圾收集)至关重要。在Java编程中,熟练掌握数组的使用能帮助开发者编写出更加高效和健壮的程序。因此,深入学习和理解数组的内存...

    易语言结构数组内存操作

    记住,虽然内存操作可以提高效率,但也需要谨慎操作,避免内存泄漏或数据损坏。 在压缩包中的“易语言结构数组内存操作源码”文件中,你可以找到具体的代码示例,这些示例会更直观地展示如何在易语言中应用上述概念...

    内存泄露测试文档

    内存泄露测试文档,提供两种不同方法供内存泄露测试。

    此解决方案是ASP.NET mvc Web应用程序,它显示每次发出新的http请求时在内存中加载数组的内存泄漏.zip

    标题中的“此解决方案是ASP.NET MVC Web应用程序,它显示每次发出新的HTTP请求时在内存中加载数组的内存泄漏”揭示了一个关键问题,即在ASP.NET MVC应用中可能存在内存泄漏问题,尤其是在处理HTTP请求时。...

    ie内存泄露监控软件

    2. **内存分析**:通过深入剖析IE的内存分配和释放行为,软件能找出可能导致内存泄露的代码段或模块,为优化和修复提供依据。 3. **泄漏定位**:当检测到内存泄露时,工具能够定位到具体的代码行,帮助开发者精准...

    1 深入理解数组内存模型 1

    "深入理解数组内存模型" 数组是一种基本的数据结构,它在...深入理解数组的内存模型可以帮助程序员更好地编写高效的程序,避免内存泄露和崩溃等问题。同时,数组的内存模型也对程序的性能和可扩展性产生了重要影响。

    测试JavaScript在IE中的内存泄露

    在压缩包子文件的文件名"IE内存泄露测试"中,我们可以推测包含的资源可能是一个测试套件、指南、或者是一个实际的工具,用于帮助开发者在IE环境下进行内存泄露的模拟和检测。 要有效地测试和解决JavaScript在IE中的...

    javascript 数组内存释放

    然而,随着程序运行,可能会产生大量的数组对象,如果不正确地管理它们,可能导致内存泄漏,影响程序性能。本篇文章将深入探讨JavaScript数组的内存释放机制,以及如何有效地管理数组内存。 首先,理解JavaScript的...

    IE内存泄露分析工具:sIEve/Drip

    标题中的“IE内存泄露分析工具:sIEve/Drip”指的是两个专门用于检测和分析Internet Explorer浏览器内存泄漏问题的工具。sIEve和Drip是独立的工具,但它们都致力于帮助开发者定位和解决IE浏览器中的内存管理问题。 ...

    Android webview 内存泄露的解决方法

    最近在activity嵌套webview显示大量图文发现APP内存一直在涨,没法释放内存,查了很多资料,大概是webview的一个BUG,引用了activity导致内存泄漏,所以就尝试传递getApplicationContext。 1.避免在xml直接写webview...

    关于数组创建以及拷贝时的内存分配

    这种内存分配通常发生在栈上,如果数组过大,可能会导致栈溢出。而在Java或C#等高级语言中,数组通常在堆上分配,以避免栈空间的限制。 内存分配不仅仅是为数组元素预留空间。它还包括为数组本身(即数组的元数据)...

    sIEve-0.0.8(IE Sieve_检测IE内存泄露情况)

    总结来说,sIEve-0.0.8是解决IE内存泄露问题的重要工具,它通过提供深入的内存分析报告,使得开发者能够更有效地调试和优化IE应用程序,从而提升用户浏览体验。对于那些仍然依赖IE或需要处理遗留IE项目的人来说,...

    ie内存泄漏检测软件

    2. **第三方工具**:JSLeaksDetector(如压缩包中的JSLeaksDetector.msi)是一款专门用于检测IE浏览器内存泄漏的工具,它可以监控JavaScript内存分配,实时报告异常增长,帮助开发者定位问题。 3. **性能分析器**:...

    Drip 检测IE内存泄漏

    4. **使用Drip**:开发者可以将Drip集成到开发流程中,在IE浏览器上运行待测试的Web应用,然后通过Drip提供的报告来定位潜在的内存泄漏问题。报告通常会包含泄漏的对象信息、内存占用量变化等关键数据。 5. **解决...

    易语言新内存数组

    这两个函数在处理新内存数组时非常关键,确保了内存的有效利用和避免内存泄漏。 在实际编程中,新内存数组常用于大数据处理、缓存、临时存储等场景。例如,在处理文件数据时,可以先用“LocalAlloc”分配内存,然后...

    Delphi new\dispose内存泄露问题解决方案

    当我们谈论"Delphi new\dispose内存泄露问题解决方案"时,我们聚焦的是如何正确地分配和释放内存,尤其是针对结构体指针和字符串类型,这是Delphi内存泄漏问题的常见来源。本文将深入探讨这个问题,并提供相应的解决...

    OpenCL将数组从内存copy到显存

    10. **释放资源**:在计算完成后,记得释放OpenCL对象以避免内存泄漏。这包括删除缓冲区、命令队列、上下文等。 总结来说,OpenCL提供了一种有效的方法,将数组从主内存移动到显存,从而充分利用GPU的并行计算能力...

Global site tag (gtag.js) - Google Analytics