最新博客站点:欢迎来访javascript
1、内存泄漏html
因为某些缘由再也不须要的内存没有被操做系统或则空闲内存池回收。编程语言中有多种管理内存的方式。这些方式从不一样程度上会减小内存泄漏的概率,高级语言嵌入了一个名为垃圾收集器的软件,其工做是跟踪内存分配和使用,以便找到再也不须要分配内存的时间,在这种状况下,它将自动释放它。然而,该过程是近似的,由于知道是否须要某些存储器的通常问题是不可断定的(不能经过算法来解决)。java
1. 循环引用致使的内存泄漏算法
当两个对象相互引用时,会造成一个循环引用,使每一个对象的引用计数为1,在纯粹的垃圾收集系统中,循环引用不是问题:若是任何其余对象都不引用所涉及的对象,则二者都是会被视为垃圾而回收。可是,在引用计数系统中,两个对象都不能被销毁,由于引用计数永远不会减到零。在使用垃圾回收和引用计数的混合系统中,因为系统没法识别循环引用而致使泄漏。在这种状况下,DOM对象和Javascript对象都不会被破坏。编程
<html> <body> <script type = "text/javascript"> document.write("Circular referances between Javascript and DOM!"); var obj; window.onload = function() { obj = document.getElementById("DivElement"); document.getElementById("DivElement").expandoProperty = obj; Array(1000).join(new Array(2000).join("XXXXX")); } </script> <div id="DivElement">Div Element</div> </body> </html>
如上面代码所示,Javascript对象obj引用了DivElement表示的DOM对象。DOM对象反过来又经过expandoProperty对Javascript对象有一个引用。Javascript对象和DOM对象之间存在循环引用。由于DOM对象经过引用计数进行管理,因此两个对象都不会被销毁。闭包
2. 外部函数引发的循环引用编程语言
下面代码中,经过调用外部函数myFunction来建立循环引用。Javascript对象和DOM对象之间的循环引用将最终致使内存泄漏。ide
<html> <head> <script type= "text/javascript"> document.write("Circular references between Javascript and DOM!"); function myFunction(element) { this.elementReferences = element; //this code forms a circular references 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>
正如上面这两类代码示例所显示的,循环很容易建立。他们还倾向于在Javascript中最方便的编程结构:闭包。函数
3. 闭包引发的内存泄漏工具
Javascript的优势之一是它容许函数嵌套在其余函数之中,嵌套内部函数能够继承外部函数的参数和变量,而且对该函数是私有的。Javascript开发人员使用内部函数将小效用函数集成到其余函数中,使得内部函数(childFunction)能够访问外部parentFunction的变量。当一个内部函数获取并使用对其外部函数变量的访问时,它称为闭包。
一个简单的闭包例子
<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是父函数closureDemoParentFunction中定义的内部函数。当用外部x的参数对closureDemoParentFunction进行调用时,外部函数变量a被赋值外部x。函数返回一个指向内部函数closureDemoInnerFunction的指针,它包含在变量x中。必须注意的是,外部函数closureDemoParentFunction的局部变量a即便在外部函数返回后也会存在。这与C++等编程语言不一样,在函数返回后,局部变量再也不存在。在Javascript中,调用closureDemoParentFunction的时刻,建立一个具备属性a的做用域对象。此属性包含paramA的值,也称为"outer x"。一样,当closureDemoParentFunction 返回时,它将返回内部函数closureDemoInnerFunction,它包含在变量x中。
因为内部函数持有对外部函数的变量的引用,所以具备属性a的做用域对象不会被垃圾回收。当在x上用一个参数值(即x("inner x")进行调用时,将弹出一个显示"outer x inner x"的警报。闭包功能强大,由于它们容许内部函数在外部函数返回后保留对外部函数变量的访问权限。遗憾的是,闭包在Javascript对象和DOM对象之间隐藏循环引用很是出色。
因为IE9以前的版本对Javascript对象和COM对象使用不一样的垃圾回收例程,所以闭包在这些版本中会致使一些特殊的问题。具体来讲,若是闭包的做用域中保存着一个HTML元素,那么就意味着该元素将没法被销毁。
function assignHandler() { var element = document.getElementById("my_btn"); element.onclick = function() { alert(element.id); }; }
以上代码建立了一个做为element元素事件处理程序的闭包,而这个闭包又建立了一个循环引用。因为匿名函数保存了一个对assignHandler()的活动对象的引用,所以就会致使没法减小element的引用数。只要匿名函数存在,element的引用数至少也是1,所以它占用的内存永远也会被回收。不过,这个问题是能够被解决的:
function assignHandler() { var element = document.getElementById("my_btn"); var id = element.id; element.onclick = function() { alert(id); }; element = null; }
上面代码,是把element.id的一个副本保存在一个变量中,而且在闭包中引用该变量消除循环引用,可是,这种程度还不能解决内存泄露的问题。必需要记住:闭包会引用包含函数的整个活动对象,而这其中包含着element。即便闭包不直接引用element,包含函数的活动对象中也仍然会保存着一个引用。所以,必需要把element变量设置为null。这样就能解除对DOM对象的引用,顺利减小引用次数,确保回收其占用的内存。
4. 事件处理程序引发的内存泄漏
在下面的代码中,你将会发现,一个JavaScript对象(obj)包含对DOM对象(由id"元素"引用)的引用的闭包。DOM元素反过来又具备对Javascript obj的引用。在Javascript对象和DOM对象之间产生的循环引用会致使内存泄漏。
<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")); }; </script> <button id="element">Click Me</button> </body> </html>
5. 避免内存泄漏的方法
在Javascript中,内存泄露的另外一方面是你能够避免它们。当您肯定了能够致使循环引用的模式时,正如前面所列举的那样,您能够开始围绕它们进行工做。咱们将使用上面三种的事件处理中内存泄漏的方式解决已知内存泄露的方法。一个简单的解决方案是使Javascript对象obj设为null,从而显式中断循环引用。
<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"); // 一些逻辑代码 }; obj.bigString=new Array(1000).join(new Array(2000).join("XXXXX")); obj = null; //显示中断循环引用 }; </script> <button id="element">"Click Here"</button> </body> </html>
另外一种方法是经过添加一个闭包,能够避免Javascript对象和DOM对象之间的循环引用。
<html> <body> <script type="text/javascript"> document.write("Avoiding memory leak via closure by adding another closure"); window.onload=function outerFunction(){ var anotherObj=function innerFunction() { 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>
第三种方法能够经过添加一个函数来避免闭包,从而防止泄漏。
<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>
6. 在Chrome中查找内存泄漏
Chrome提供了一系列优秀的工具来分析JavaScript代码的内存使用。涉及与内存相关的两幅图:timeline视图和profile视图。
参考:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management#Release_when_the_memory_is_not_needed_anymore
更多内容请参考:
http://www.ruanyifeng.com/blog/2017/04/memory-leak.html