一区二区久久-一区二区三区www-一区二区三区久久-一区二区三区久久精品-麻豆国产一区二区在线观看-麻豆国产视频

權(quán)威JavaScript 中的內(nèi)存泄露模式

作者:
Abhijeet Bhattacharya (abhbhatt@in.ibm.com), 系統(tǒng)軟件工程師, IBM India
Kiran Shivarama Shivarama Sundar (kisundar@in.ibm.com), 系統(tǒng)軟件工程師, IBM India

2007 年 5 月 28 日

如果您知道內(nèi)存泄漏的起因,那么在 JavaScript 中進(jìn)行相應(yīng)的防范就應(yīng)該相當(dāng)容易。在這篇文章中,作者 Kiran Sundar 和 Abhijeet Bhattacharya 將帶您親歷 JavaScript 中的循環(huán)引用的全部基本知識(shí),向您介紹為何它們會(huì)在某些瀏覽器中產(chǎn)生問(wèn)題,尤其是在結(jié)合了閉包的情況下。在了解了您應(yīng)該引起注意的常見(jiàn)內(nèi)存泄漏模式之后,您還將學(xué)到應(yīng)對(duì)這些泄漏的諸多方法。

JavaScript 是用來(lái)向 Web 頁(yè)面添加動(dòng)態(tài)內(nèi)容的一種功能強(qiáng)大的腳本語(yǔ)言。它尤其特別有助于一些日常任務(wù),比如驗(yàn)證密碼和創(chuàng)建動(dòng)態(tài)菜單組件。JavaScript 易學(xué)易用,但卻很容易在某些瀏覽器中引起內(nèi)存的泄漏。在這個(gè)介紹性的文章中,我們解釋了 JavaScript 中的泄漏由何引起,展示了常見(jiàn)的內(nèi)存泄漏模式,并介紹了如何應(yīng)對(duì)它們。

注意本文假設(shè)您已經(jīng)非常熟悉使用 JavaScript 和 DOM 元素來(lái)開(kāi)發(fā) Web 應(yīng)用程序。本文尤其適合使用 JavaScript 進(jìn)行 Web 應(yīng)用程序開(kāi)發(fā)的開(kāi)發(fā)人員,也可供有興趣創(chuàng)建 Web 應(yīng)用程序的客戶提供瀏覽器支持以及負(fù)責(zé)瀏覽器故障排除的人員參考。

JavaScript 中的內(nèi)存泄漏

JavaScript 是一種垃圾收集式語(yǔ)言,這就是說(shuō),內(nèi)存是根據(jù)對(duì)象的創(chuàng)建分配給該對(duì)象的,并會(huì)在沒(méi)有對(duì)該對(duì)象的引用時(shí)由瀏覽器收回。JavaScript 的垃圾收集機(jī)制本身并沒(méi)有問(wèn)題,但瀏覽器在為 DOM 對(duì)象分配和恢復(fù)內(nèi)存的方式上卻有些出入。

InterNET Explorer 和 Mozilla Firefox 均使用引用計(jì)數(shù)來(lái)為 DOM 對(duì)象處理內(nèi)存。在引用計(jì)數(shù)系統(tǒng),每個(gè)所引用的對(duì)象都會(huì)保留一個(gè)計(jì)數(shù),以獲悉有多少對(duì)象正在引用它。如果計(jì)數(shù)為零,該對(duì)象就會(huì)被銷毀,其占用的內(nèi)存也會(huì)返回給堆。雖然這種解決方案總的來(lái)說(shuō)還算有效,但在循環(huán)引用方面卻存在一些盲點(diǎn)。

循環(huán)引用的問(wèn)題何在?

當(dāng)兩個(gè)對(duì)象互相引用時(shí),就構(gòu)成了循環(huán)引用,其中每個(gè)對(duì)象的引用計(jì)數(shù)值都被賦 1。在純垃圾收集系統(tǒng)中,循環(huán)引用問(wèn)題不大:若涉及到的兩個(gè)對(duì)象中的一個(gè)對(duì)象被任何其他對(duì)象引用,那么這兩個(gè)對(duì)象都將被垃圾收集。而在引用計(jì)數(shù)系統(tǒng),這兩個(gè)對(duì)象都不能被銷毀,原因是引用計(jì)數(shù)永遠(yuǎn)不能為零。在同時(shí)使用了垃圾收集和引用計(jì)數(shù)的混合系統(tǒng)中,將會(huì)發(fā)生泄漏,因?yàn)橄到y(tǒng)不能正確識(shí)別循環(huán)引用。在這種情況下,DOM 對(duì)象和 JavaScript 對(duì)象均不能被銷毀。清單 1 顯示了在 JavaScript 對(duì)象和 DOM 對(duì)象間存在的一個(gè)循環(huán)引用。

清單 1. 循環(huán)引用導(dǎo)致了內(nèi)存泄漏

<html>   	<body>   	<script type="text/Javascript">   	document.write("circular references between JavaScript and DOM!");   	var obj;   	window.onload = function(){		obj=document.getElementById("DivElement");      	document.getElementById("DivElement").expandoProperty=obj;      	obj.bigString=new Array(1000).join(new Array(2000).join("XXXXX"));      	};   	</script>   	<div id="DivElement">Div Element</div>   	</body>   	</html>

如上述清單中所示,JavaScript 對(duì)象 obj 擁有到 DOM 對(duì)象的引用,表示為 DivElement。而 DOM 對(duì)象則有到此 JavaScript 對(duì)象的引用,由 expandoProperty 表示。可見(jiàn),JavaScript 對(duì)象和 DOM 對(duì)象間就產(chǎn)生了一個(gè)循環(huán)引用。由于 DOM 對(duì)象是通過(guò)引用計(jì)數(shù)管理的,所以兩個(gè)對(duì)象將都不能銷毀。

另一種內(nèi)存泄漏模式

在清單 2 中,通過(guò)調(diào)用外部函數(shù) myFunction 創(chuàng)建循環(huán)引用。同樣,JavaScript 對(duì)象和 DOM 對(duì)象間的循環(huán)引用也會(huì)導(dǎo)致內(nèi)存泄漏。

清單 2. 由外部函數(shù)調(diào)用引起的內(nèi)存泄漏

<html>	<head>	<script type="text/Javascript">	document.write(" object s between JavaScript and DOM!");	function myFunction(element)	{		this.elementReference = element;		// This code forms a circular reference here		//by DOM-->JS-->DOM		element.expandoProperty = this;	}	function Leak() {		//This code will leak		new myFunction(document.getElementById("myDiv"));	}	</script>	</head>	<body onload="Leak()">	<div id="myDiv"></div>	</body>	</html>

正如這兩個(gè)代碼示例所示,循環(huán)引用很容易創(chuàng)建。在 JavaScript 最為方便的編程結(jié)構(gòu)之一:閉包中,循環(huán)引用尤其突出。

JavaScript 中的閉包

JavaScript 的過(guò)人之處在于它允許函數(shù)嵌套。一個(gè)嵌套的內(nèi)部函數(shù)可以繼承外部函數(shù)的參數(shù)和變量,并由該外部函數(shù)私有。清單 3 顯示了內(nèi)部函數(shù)的一個(gè)示例。


清單 3. 一個(gè)內(nèi)部函數(shù)

function parentFunction(paramA)	{  		var a = paramA;  		function childFunction()  		{			return a + 2;  		}  		return childFunction();	}

JavaScript 開(kāi)發(fā)人員使用內(nèi)部函數(shù)來(lái)在其他函數(shù)中集成小型的實(shí)用函數(shù)。如清單 3 所示,此內(nèi)部函數(shù) childFunction 可以訪問(wèn)外部函數(shù) parentFunction 的變量。當(dāng)內(nèi)部函數(shù)獲得和使用其外部函數(shù)的變量時(shí),就稱其為一個(gè)閉包

了解閉包

考慮如清單 4 所示的代碼片段。


清單 4. 一個(gè)簡(jiǎn)單的閉包

<html>	<body>	<script type="text/Javascript">	document.write("Closure Demo!!");	window.onload=	function closureDemoParentFunction(paramA)	{  		var a = paramA;  		return function closureDemoInnerFunction (paramB)  		{   			alert( a +" "+ paramB);  		};	};	var x = closureDemoParentFunction("outer x");	x("inner x");	</script>	</body>	</html>

在上述清單中,closureDemoInnerFunction 是在父函數(shù) closureDemoParentFunction 中定義的內(nèi)部函數(shù)。當(dāng)用外部的 x 對(duì) closureDemoParentFunction 進(jìn)行調(diào)用時(shí),外部函數(shù)變量 a 就會(huì)被賦值為外部的 x。函數(shù)會(huì)返回指向內(nèi)部函數(shù) closureDemoInnerFunction 的指針,該指針包括在變量 x 內(nèi)。

外部函數(shù) closureDemoParentFunction 的本地變量 a 即使在外部函數(shù)返回時(shí)仍會(huì)存在。這一點(diǎn)不同于 C/C++ 這樣的編程語(yǔ)言,在 C/C++ 中,一旦函數(shù)返回,本地變量也將不復(fù)存在。在 JavaScript 中,在調(diào)用 closureDemoParentFunction 的時(shí)候,帶有屬性 a 的范圍對(duì)象將會(huì)被創(chuàng)建。該屬性包括值 paramA,又稱為“外部 x”。同樣地,當(dāng) closureDemoParentFunction 返回時(shí),它將會(huì)返回內(nèi)部函數(shù) closureDemoInnerFunction,該函數(shù)包括在變量 x 中。

由于內(nèi)部函數(shù)持有到外部函數(shù)的變量的引用,所以這個(gè)帶屬性 a 的范圍對(duì)象將不會(huì)被垃圾收集。當(dāng)對(duì)具有參數(shù)值 inner xx 進(jìn)行調(diào)用時(shí),即 x("inner x"),將會(huì)彈出警告消息,表明 “outer x innerx”。

清單 4 簡(jiǎn)要解釋了 JavaScript 閉包。閉包功能非常強(qiáng)大,原因是它們使內(nèi)部函數(shù)在外部函數(shù)返回時(shí)也仍然可以保留對(duì)此外部函數(shù)的變量的訪問(wèn)。不幸的是,閉包非常易于隱藏 JavaScript 對(duì)象 和 DOM 對(duì)象間的循環(huán)引用。

閉包和循環(huán)引用

在清單 5 中,可以看到一個(gè)閉包,在此閉包內(nèi),JavaScript 對(duì)象(obj)包含到 DOM 對(duì)象的引用(通過(guò) id "element" 被引用)。而 DOM 元素則擁有到 JavaScript obj 的引用。這樣建立起來(lái)的 JavaScript 對(duì)象和 DOM 對(duì)象間的循環(huán)引用將會(huì)導(dǎo)致內(nèi)存泄漏。


清單 5. 由事件處理引起的內(nèi)存泄漏模式

<html>	<body>	<script type="text/Javascript">	document.write("Program to illustrate memory leak via closure");	window.onload=function outerFunction(){		var obj = document.getElementById("element");		obj.onclick=function innerFunction(){		alert("Hi! I will leak");		};		obj.bigString=new Array(1000).join(new Array(2000).join("XXXXX"));		// This is used to make the leak significant	};	</script>	<button id="element">Click Me</button>	</body>	</html>

避免內(nèi)存泄漏

幸好,JavaScript 中的內(nèi)存泄漏是可以避免的。當(dāng)確定了可導(dǎo)致循環(huán)引用的模式之后,正如我們?cè)谏鲜稣鹿?jié)中所做的那樣,您就可以開(kāi)始著手應(yīng)對(duì)這些模式了。這里,我們將以上述的 由事件處理引起的內(nèi)存泄漏模式 為例來(lái)展示三種應(yīng)對(duì)已知內(nèi)存泄漏的方式。

一種應(yīng)對(duì) 清單 5 中的內(nèi)存泄漏的解決方案是讓此 JavaScript 對(duì)象 obj 為空,這會(huì)顯式地打破此循環(huán)引用,如清單 6 所示。

 清單 6. 打破循環(huán)引用

<html>	<body>	<script type="text/Javascript">	document.write("Avoiding memory leak via closure by breaking the circular  reference");		window.onload=function outerFunction(){		var obj = document.getElementById("element");		obj.onclick=function innerFunction()		{			alert("Hi! I have avoided the leak");			// Some logic here		};		obj.bigString=new Array(1000).join(new Array(2000).join("XXXXX"));		obj = null; //This breaks the circular reference		};	</script>	<button id="element">"Click Here"</button>	</body>	</html>

清單 7 是通過(guò)添加另一個(gè)閉包來(lái)避免 JavaScript 對(duì)象和 DOM 對(duì)象間的循環(huán)引用。

清單 7. 添加另一個(gè)閉包

<html>	<body>	<script type="text/Javascript">	document.write("Avoiding a memory leak by adding another closure"); 	window.onload=function outerFunction(){	var anotherObj = function innerFunction()			 {				// Some logic here				alert("Hi! I have avoided the leak");		 	 };		 (function anotherInnerFunction(){			var obj = document.getElementById("element");			obj.onclick=anotherObj })();		  };	</script>	<button id="element">"Click Here"</button>	</body>	</html>

清單 8 則通過(guò)添加另一個(gè)函數(shù)來(lái)避免閉包本身,進(jìn)而阻止了泄漏。

清單 8. 避免閉包自身

<html>	<head>	<script type="text/Javascript">	document.write("Avoid leaks by avoiding closures!");	window.onload=function()	{		var obj = document.getElementById("element");		obj.onclick = doesNotLeak;	}	function doesNotLeak()	{		//Your Logic here		alert("Hi! I have avoided the leak");	}	</script>	</head>	<body>	<button id="element">"Click Here"</button>	</body>	</html>

結(jié)束語(yǔ)

本文解釋了循環(huán)引用是如何導(dǎo)致 JavaScript 中的內(nèi)存泄漏的 ―― 尤其是在結(jié)合了閉包的情況下。您還了解了涉及到循環(huán)引用的一些常見(jiàn)內(nèi)存泄漏模式以及應(yīng)對(duì)這些泄漏模式的幾種簡(jiǎn)單方式。

作者簡(jiǎn)介

Abhijeet Bhattacharya photo

Abhijeet Bhattacharya 是 IBM 印度軟件實(shí)驗(yàn)室的一名系統(tǒng)工程師。在過(guò)去三年中,他一直是 OS/2 IBM Web Browser 支持團(tuán)隊(duì)中的一員。他也具有系統(tǒng)管理領(lǐng)域的相關(guān)經(jīng)驗(yàn),并參與過(guò) IBM Pegasus 開(kāi)源創(chuàng)新項(xiàng)目。他目前工作的重點(diǎn)包括分布式計(jì)算和 SARPC。他擁有 Rajiv Gandhi Technical University 的工程學(xué)士學(xué)位。


Kiran Shivarama Sundar photo

Kiran Shivarama Sundar 是 IBM 印度軟件實(shí)驗(yàn)室的一名系統(tǒng)工程師。在過(guò)去三年中,他一直是 OS/2 IBM Web Browser 支持團(tuán)隊(duì)中的一員。他同時(shí)也具有諸多其他項(xiàng)目的工作經(jīng)驗(yàn),包括為 Apache Tuscany Open Source Project 開(kāi)發(fā)命令行工具以及為 IBM 的 EPCIS 團(tuán)隊(duì)開(kāi)發(fā) RFIDIC Installer。目前,Kiran 加入了 IBM WebSphere Adapters 支持團(tuán)隊(duì),負(fù)責(zé)提供對(duì) JMS 和 MQ 適配器的支持。他已成功獲得了 Sun Certified Java Programmer、Sun Certified Web Component Developer 和 Sun Certified Business Component Developer 的認(rèn)證。他目前所關(guān)注的領(lǐng)域包括 Java、J2EE、Web 服務(wù)和 SOA。他擁有 Visweshwaraya Technology University 的工程學(xué)士學(xué)位。

JavaScript技術(shù)權(quán)威JavaScript 中的內(nèi)存泄露模式,轉(zhuǎn)載需保留來(lái)源!

鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請(qǐng)第一時(shí)間聯(lián)系我們修改或刪除,多謝。

主站蜘蛛池模板: 国久久 | 国产91精品福利在线观看 | 色综合图片二区150p | 91精品国产闺蜜国产在线 | 亚洲一区二区三区播放在线 | 99久久精品免费看国产一区二区三区 | 欧美性在线观看 | 美女视频很黄很a免费国产 美女视频黄.免费网址 | 亚洲国产精品免费视频 | 日韩美女毛片 | 亚洲国产精品激情在线观看 | 欧美人最猛性xxxxx | 亚洲综合色吧 | 精品久久久久久久一区二区手机版 | 一区二区三区四区在线不卡高清 | 91精品国产91久久久久 | 99午夜 | 亚洲一区日韩一区欧美一区a | 女女同性一区二区三区四区 | 久久婷婷久久一区二区三区 | 日本一区二区三区久久久 | 国产91高清 | 91短视频在线高清hd | 青青草国产97免久久费观看 | 成人免费观看黄a大片夜月 成人免费观看视频 | 亚洲一区第一页 | 欧美日韩亚洲一区二区三区 | 美女一级毛片毛片在线播放 | 国产精品免费久久久久影院小说 | 国产成人精品一区二三区 | 欧美精品一国产成人性影视 | 成人在线第一页 | 2019天天干 | 国产精品资源在线观看 | 国产成人精品日本亚洲网站 | 色欧美色| 国产精品综合一区二区 | 国产精品伦理一区二区三区 | 中文字幕一区婷婷久久 | 日本乱人伦片中文字幕三区 | 亚洲狠狠狠一区二区三区 |