Memory leak patterns in JavaScript
Handling circular references in JavaScript applications
Plugging memory leaks in JavaScript is easy enough when you know what causes them. In this article authors Kiran Sundar and Abhijeet Bhattacharya walk you through the basics of circular references in JavaScript and explain why they can cause problems in certain browsers, especially when combined with closures. After seeing some of the common memory leak patterns you should watch out for, you'll learn a variety of easy ways to work around them
avaScript 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. See Resources 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
<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 the expandoProperty
. 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
<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
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 functionchildFunction
has access to the variables of the outer parentFunction
. 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
<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 function closureDemoParentFunction
. 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 moment closureDemoParentFunction
is called, a scope object with property a is created. This property contains the value ofparamA, also known as "outer x". Similarly, when the closureDemoParentFunction
returns, it will return the inner functionclosureDemoInnerFunction
, 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
<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
<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
<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
<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 Resources to learn more about the topics discussed in this introductory article.
Resources
Learn
- "JavaScript and the Document Object Model" (Nicholas Chase, developerWorks, July 2002): Introduces the DOM to JavaScript developers.
- "Crossing borders: Closures" (Bruce Tate, developerWorks, January 2007): A primer on the many uses of closures (based on Ruby but conceptually applicable to JavaScript).
- "Finite state machines in JavaScript, Part 1: Design a widget" (Edward J. Pring, developerWorks, January 2007): A fun exercise using closures and other advanced features of JavaScript.
- "A re-introduction to javascript" (Simon Wilson, Mozilla.org): A closer look at JavaScript and its features.
- "Using XPCOM in JavaScript without leaking" (David Baron, Mozilla.org): Explains why and how Firefox uses reference counting for memory allocation.
- "Circular references to DOM objects on an HTML page cause a memory leak" (Microsoft Help and Support): What Microsoft has to say about memory leaks in IE.
- "Memory leakage in Internet Explorer -- revisited" (Volkan Ozcelik, The Code Project, November 2005): A tutorial introduction to the common causes of memory leaks in JavaScript, with reference to IE.
- "developerWorks Web technology zone: Resources for Web 2.0, Ajax, wikis, PHP, mashups, and other Web projects.
Get products and technologies
- IBM product evaluation versions: Get your hands on application development tools and middleware products from DB2®, Lotus®, Rational®, Tivoli®, and WebSphere®.
Discuss
- Participate in the discussion forum.
- developerWorks blogs: Get involved in the developerWorks community.
Memory leak patterns in JavaScript的更多相关文章
- JavaScript :memory leak [转]
Memory leak patterns in JavaScript Handling circular references in JavaScript applications Abhijeet ...
- Linux C/C++ Memory Leak Detection Tool
目录 . 内存使用情况分析 . 内存泄漏(memory leak) . Valgrind使用 1. 内存使用情况分析 0x1: 系统总内存的分析 可以从proc目录下的meminfo文件了解到当前系统 ...
- A Cross-Platform Memory Leak Detector
Memory leakage has been a permanent annoyance for C/C++ programmers. Under MSVC, one useful feature ...
- malloc(50) 内存泄露 内存溢出 memory leak会最终会导致out of memory
https://en.wikipedia.org/wiki/Memory_leak In computer science, a memory leak is a type of resource l ...
- Android 内存管理 &Memory Leak & OOM 分析
转载博客:http://blog.csdn.net/vshuang/article/details/39647167 1.Android 进程管理&内存 Android主要应用在嵌入式设备当中 ...
- quartz集群报错but has failed to stop it. This is very likely to create a memory leak.
quartz集群报错but has failed to stop it. This is very likely to create a memory leak. 在一台配置1核2G内存的阿里云服务器 ...
- 山东省第七届ACM省赛------Memory Leak
Memory Leak Time Limit: 2000MS Memory limit: 131072K 题目描述 Memory Leak is a well-known kind of bug in ...
- caching redirect views leads to memory leak (Spring 3.1)
在Spring 3.1以及以下版本使用org.springframework.web.servlet.view.UrlBasedViewResolver + cache(如下配置),在会出现任意种re ...
- 一则JVM memory leak解决的过程
起因是我们的集群应用(3台机器)新版本测试过程中,一般的JVM内存占用 都在1G左右, 但在运行了一段时间后,慢慢升到了4G, 这是一个明显不正常的现象. 定位 过程: 1.先在该机器上按照步骤尝试重 ...
随机推荐
- Linux 系统 /proc/[pid]/stat 文件解释
转载:http://www.net527.cn/a/caozuoxitong/Linux/2012/0823/24385.html [root@localhost ~]# cat /proc/6873 ...
- [mark]如何删除地址栏的记录?
比如,我输入字母a ..因为经常访问.它首先会自动帮我补填 amazon.cn 第二行出现 ali213...第三行是 alipay... 这个很简单,没必要搞得很复杂很Geek.比如楼主的情况,首先 ...
- 史上最全的Unity面试题(持续更新总结。。。。。。) 包含答案的Unity面试题
这个是我刚刚整理出的Unity面试题,为了帮助大家面试,同时帮助大家更好地复习Unity知识点,如果大家发现有什么错误,(包括错别字和知识点),或者发现哪里描述的不清晰,请在下面留言,我会重新更新,希 ...
- linux下批量添加新用户
作为一个系统管理员,可能要经常的为机器添加用户,可能是在一台机器上添加很多用户,也有可能是在很多机器上要添加同一个用户. 基本上,批量添加用户有两种方式,一是用useradd + passwd命令配合 ...
- Informatica 常用组件Source Qualifier之三 联接查询
联接源数据 可以使用一个源限定符转换来联接来自多个关系表的数据.这些表必须能从相同的实例或数据库服务器访问.当映射使用相关的关系源时,您可以在一个源限定符转换中同时联接两个源.在会话期间,源数据库在传 ...
- html input type=date 赋值问题 必须yyyy-mm-dd格式
type=date ,日期类型默认格式是yyyy-mm-dd 因此必须给该控件赋值yyyy-mm-dd格式的数据 错误的赋值 <input type="date" id=&q ...
- Linux laravel安装
第一步:安装php套件 目前为止laravel是5.1版本,需要对php有要求,要php5.59以上 The Laravel framework has a few system requiremen ...
- Oracle数据库导入dmp文件报错处理方法
在向oracle数据库执行导入命令的时候报错,错误如下,大概意思是TNS中找不到服务名 下面说一下解决步骤 1:进入oracle用户,使用cat查看.bash_profile文件,找到ORACLE_H ...
- C++ 相关面试题汇总
多态性与虚函数 (陈维兴教材) (1)所谓多态性就是不同对象在收到相同的消息时,产生不同的动作.直观的说,多态性是指用一个名字定义不同的函数,这些函数执行不同但又类似的操作,从而可以使用相同的方式来调 ...
- [Grunt] Development Automation Tasks with Grunt
With Grunt you can automate core tasks for your AngularJS project. In this lesson we will take a loo ...