JavaScript :memory leak [转]

Memory leak patterns in JavaScript

Handling circular references in JavaScript applications

Abhijeet Bhattacharya and Kiran Shivarama Sundar
Published on April 24, 2007

FacebookTwitterLinked InGoogle+E-mail this page

6

JavaScript is a powerful scripting language used to add dynamic content to Web pages. It is especially beneficial for everyday tasks such as password validation and creating dynamic menu components. While JavaScript is easy to learn and write, it is prone to memory leaks in certain browsers. In this introductory article we explain what causes memory leaks in JavaScript, demonstrate some of the common memory leak patterns to watch out for, and show you how to work around them.

Note that the article assumes you are familiar with using JavaScript and DOM elements to develop Web applications. The article will be most useful to developers who use JavaScript for Web application development. It might also serve as a reference for those providing browser support to clients rolling out Web applications or for anyone tasked with troubleshooting browser issues.

Is my browser leaking?

Internet Explorer and Mozilla Firefox are the two Web browsers most commonly associated with memory leaks in JavaScript. The culprit in both browsers is the component object model used to manage DOM objects. Both the native Windows COM and Mozilla‘s XPCOM use reference-counting garbage collection for memory allocation and retrieval. Reference counting is not always compatible with the mark-and-sweep garbage collection used for JavaScript. This article focuses on ways to work around memory leaks in JavaScript code. SeeRelated topics to learn more about COM layer memory handling in Firefox and IE.

Memory leaks in JavaScript

JavaScript is a garbage collected language, meaning that memory is allocated to objects upon their creation and reclaimed by the browser when there are no more references to them. While there is nothing wrong with JavaScript‘s garbage collection mechanism, it is at odds with the way some browsers handle the allocation and recovery of memory for DOM objects.

Internet Explorer and Mozilla Firefox are two browsers that use reference counting to handle memory for DOM objects. In a reference counting system, each object referenced maintains a count of how many objects are referencing it. If the count becomes zero, the object is destroyed and the memory is returned to the heap. Although this solution is generally very efficient, it has a blind spot when it comes to circular (or cyclic) references.

What‘s wrong with circular references?

A circular reference is formed when two objects reference each other, giving each object a reference count of 1. In a purely garbage collected system, a circular reference is not a problem: If neither of the objects involved is referenced by any other object, then both are garbage collected. In a reference counting system, however, neither of the objects can be destroyed, because the reference count never reaches zero. In a hybrid system, where both garbage collection and reference counting are being used, leaks occur because the system fails to identify a circular reference. In this case, neither the DOM object nor the JavaScript object is destroyed. Listing 1 shows a circular reference between a JavaScript object and a DOM object.

Listing 1. A circular reference resulting in a memory leak

1

2

3

4

5

6

7

8

9

10

11

12

13

14

<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>

As you can see in the above listing, the JavaScript object obj has a reference to the DOM object represented by DivElement. The DOM object, in turn, has a reference to the JavaScript object through theexpandoProperty. A circular reference exists between the JavaScript object and the DOM object. Because DOM objects are managed through reference counting, neither object will ever be destroyed.

Another memory leak pattern

In Listing 2 a circular reference is created by calling the external function myFunction. Once again the circular reference between a JavaScript object and a DOM object will eventually lead to a memory leak.

Listing 2. A memory leak caused by calling an external function

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

<html>

<head>

<script type="text/javascript">

document.write("Circular references 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>

As these two code samples show, circular references are easy to create. They also tend to crop up quite a bit in one of JavaScript‘s most convenient programming constructs: closures.

Closures in JavaScript

One of JavaScript‘s strengths is that it allows functions to be nested within other functions. A nested, or inner, function can inherit the arguments and variables of its outer function, and is private to that function. Listing 3 is an example of an inner function.

Listing 3. An inner function

1

2

3

4

5

6

7

8

9

function parentFunction(paramA)

{

        var a = paramA;

        function childFunction()

        {

        return a + 2;

        }

        return childFunction();

}

JavaScript developers use inner functions to integrate small utility functions within other functions. As you can see in Listing 3, the inner function childFunction has access to the variables of the outerparentFunction. When an inner function gains and uses access to its outer function‘s variables it is known as a closure.

Learning about closures

Consider the code snippet shown in Listing 4.

Listing 4. A simple closure

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

<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>

In the above listing closureDemoInnerFunction is the inner function defined within the parent functionclosureDemoParentFunction. When a call is made to closureDemoParentFunction with a parameter of outer x, the outer function variable a is assigned the value outer x. The function returns with a pointer to the inner function closureDemoInnerFunction, which is contained in the variable x.

It is important to note that the local variable a of the outer function closureDemoParentFunction will exist even after the outer function has returned. This is different from programming languages such as C/C++, where local variables no longer exist once a function has returned. In JavaScript, the momentclosureDemoParentFunction is called, a scope object with property a is created. This property contains the value of paramA, also known as "outer x". Similarly, when the closureDemoParentFunction returns, it will return the inner function closureDemoInnerFunction, which is contained in the variable x.

Because the inner function holds a reference to the outer function‘s variables, the scope object with property a will not be garbage collected. When a call is made on x with a parameter value of inner x -- that is,x("inner x") -- an alert showing "outer x innerx" will pop up.

Listing 4 is a very simple illustration of a JavaScript closure. Closures are powerful because they enable inner functions to retain access to an outer function‘s variables even after the outer function has returned. Unfortunately, closures are excellent at hiding circular references between JavaScript objects and DOM objects.

Closures and circular references

In Listing 5 you see a closure in which a JavaScript object (obj) contains a reference to a DOM object (referenced by the id "element"). The DOM element, in turn, has a reference to the JavaScript obj. The resulting circular reference between the JavaScript object and the DOM object causes a memory leak.

Listing 5. Event handling memory leak pattern

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

<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>

Avoiding memory leaks

The upside of memory leaks in JavaScript is that you can avoid them. When you have identified the patterns that can lead to circular references, as we‘ve done in the previous sections, you can begin to work around them. We‘ll use the above event-handling memory leak pattern to demonstrate three ways to work around a known memory leak.

One solution to the memory leak in Listing 5 is to make the JavaScript object obj null, thus explicitly breaking the circular reference, as shown in Listing 6.

Listing 6. Break the circular reference

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

<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>

In Listing 7 you avoid the circular reference between the JavaScript object and the DOM object by adding another closure.

Listing 7. Add another closure

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

<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>

In Listing 8 you avoid the closure itself by adding another function, thereby preventing the leak.

Listing 8. Avoid the closure altogether

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

<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>

In conclusion

This article has explained how circular references can lead to memory leaks in JavaScript, particularly when combined with closures. You‘ve seen several common memory leak patterns involving circular references and some easy ways to work around them. See Related topics to learn more about the topics discussed in this introductory article.

http://www.ibm.com/developerworks/web/library/wa-memleak/?S_TACT=105AGX52&S_CMP=cn-a-wa

时间: 2024-10-26 16:33:36

JavaScript :memory leak [转]的相关文章

replaceThing 闭包泄漏(A surprising JavaScript memory leak found at Meteor)

JavaScript是一种隐蔽的功能性编程语言,其函数是闭包(封闭):函数对象可以访问其封闭作用域中定义的变量,即使该作用域已经完成. 一旦它们定义的函数已经完成,并且在其作用域内定义的所有函数本身都被 GCed(垃圾收集),那么由闭包捕获的局部变量就被垃圾收集. var run = function () { var str = new Array(1000000).join('*'); var doSomethingWithStr = function () { if (str === 's

Linux C/C++ Memory Leak Detection Tool

目录 1. 内存使用情况分析 2. 内存泄漏(memory leak) 3. Valgrind使用 1. 内存使用情况分析 0x1: 系统总内存的分析 可以从proc目录下的meminfo文件了解到当前系统内存的使用情况汇总,其中可用的物理内存 = memfree + buffers + cached当memfree不够时,内核会通过回写机制(pdflush线程)把cached和buffered内存回写到后备存储器,从而释放相关内存供进程使用,或者通过手动方式显式释放cache内存:echo 3

Memory Leak Detection in Embedded Systems

One of the problems with developing embedded systems is the detection of memory leaks; I've found three tools that are useful for this. These tools are used to detect application program errors, not kernel memory leaks. Two of these tools (mtrace and

To prevent a memory leak, the JDBC Driver has been forcibly unregistered.

1.错误描述 严重: The web application [/AMST] registered the JDBC driver [org.logicalcobwebs.proxool.ProxoolDriver] but failed to unregister it when the web application was stopped. To prevent a memory leak, the JDBC Driver has been forcibly unregistered. 八

【Valgrind】How to check memory leak and where it&#39;s in 10 mins

1. Install sudo apt-get install valgrind 2. If memory leak example code: /* memleak.c */ #include <stdlib.h> void* memleak(int n) { void *p = malloc(n); return p; } memleak.c /* main.c */ #include <stdio.h> #include <stdlib.h> void* meml

警告: The web application [ROOT] appears to have started a thread named [Thread-48] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:

1. 问题描述 tomcat跑web项目(其中依赖java项目) 出现大量上述警告 项目起不来 关键字 memory leak 内存泄漏 2. 解决方案 难道是程序写的有问题? 最终 将tomcat VM参数中 内存调大 解决了 -Xms512m -Xmx512m -Xmn300m -Xss2048k -XX:PermSize=512m -XX:MaxPermSize=512m 需要根据机器的配置做相应调整

内存泄漏(memory leak)和内存溢出

1. 什么是内存泄漏(memory leak)? 指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况.内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费. 2. 两种类型的内存泄漏: 堆内存泄漏(Heap leak).对内存指的是程序运行中根据需要分配通过malloc,realloc new等从堆中分配的一块内存,再是完成后必须通过调用对应的 free或者delete 删掉.如果程序的设计的错误导致这部分内存没有被释放,

Memory Leak Detection in C++

原文链接:http://www.linuxjournal.com/article/6556?page=0,0 An earlier article [“Memory Leak Detection in Embedded Systems”, LJ, September 2002, available atwww.linuxjournal.com/article/6059] discussed the detection of memory leaks when using C as the pro

内存溢出(Oom)和内存泄露(Memory leak)

内存溢出(Oom):运行内存大于可用内存的情况.比如申请了一个integer空间,结果存放下了只有long才能存放的数据 内存泄露(Memory leak):程序员忘记释放已用内存的情况,是内存管理较为常见的现象 以发生的方式来分类,内存泄漏可以分为4类: 1. 常发性内存泄漏.发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏. 2. 偶发性内存泄漏.发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生.常发性和偶发性是相对的.对于特定的环境,偶发性的也许就变成了常发性