IE9關聯數組致使內存泄漏測試報告

最近爲了知足一部分朋友的需求,給 zTree 提供了 destroy 的方法,用於讓 zTree 自行清空。爲了檢查該方法是否有效,作了一個簡單的測試——顯示5000個節點而後清空,此操做循環100次,結果發現 IE9 下內存嚴重暴漲,因而進行了反覆篩查,最終鎖定了嫌疑犯:關聯數組(data[key] = value)致使的內存泄漏!json

只找到了嫌疑犯不行,定罪要有證據的,設計了一個簡單的模型專門進行這個狀況的測試,不排除可能因爲個人疏忽獲得的錯誤的結論,所以很是歡迎你們踊躍發表本身的見解,隨便噴吧。。。。數組

【測試模型】瀏覽器

不要DOM和閉包、匿名函數干擾,一個全局變量 data 用於保存生成的數據;一個全局變量max用於設置數據最大條數;一個Function用於建立數據;一個Function 用於銷燬數據。兩個按鈕,分別用於觸發這兩個Function。閉包

補充:爲了進行多種狀況對比,所以一共製做了8組建立數據、銷燬數據的 Function ,詳細見後面的說明。架構

【測試流程】函數


【測試環境】性能

Win7 64位操做系統;IE Tester下的 IE六、八、9;FireFox v14.0.1;Chrome v21.0.1180.79;IE6性能太差,所以設置數據條數 max=100000;其餘的瀏覽器都設置 max=1000000;測試

【開始測試】spa

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 就讓咱們改變整個代碼的架構嗎???
相關文章
相關標籤/搜索