- 浏览: 296822 次
- 性别:
- 来自: 南京
文章分类
最新评论
-
bo_hai:
哎,不是原创呀!
dwr学习 -
JonyUabka:
呵呵,蛮有创意的哦~
Myeclipse中启动Weblogic9异常解决 zz -
wwwmmmbird:
1楼没有license.bea,下载一个。
Myeclipse中启动Weblogic9异常解决 zz -
wwwmmmbird:
非常感谢楼主,问题已解决!
<2010-8-20 上午0 ...
Myeclipse中启动Weblogic9异常解决 zz -
hnicypb:
收下。。。最近准备装个玩下。。。
楼主辛苦
[WinXP+VMware+Ubuntu]安装+VMwareTools+输入法+系统美化全攻略 zz
Memory Leakage in Internet Explorer - revisited
By volkan.ozcelik.
In this article, we will review JavaScript memory leakage patterns from a slightly different perspective and support it with diagrams and memory utilization graphs.
Introduction
If you are developing client-side re-usable scripting objects, sooner or later you will find yourself spotting out memory leaks. Chances are that your browser will suck memory like a sponge and you will hardly be able to find a reason why your lovely DHTML navigation's responsiveness decreases severely after visiting a couple of pages within your site.
A Microsoft developer Justing Rogers has described IE leak patterns in his excellent article.
In this article, we will review those patterns from a slightly different perspective and support it with diagrams and memory utilization graphs. We will also introduce several subtler leak scenarios. Before we begin, I strongly recommend you to read that article if you have not already read.
Why does the memory leak?
The problem of memory leakage is not just limited to Internet Explorer. Almost any browser (including but not limited to Mozilla, Netscape and Opera) will leak memory if you provide adequate conditions (and it is not that hard to do so, as we will see shortly). But (in my humble opinion, ymmv etc.) Internet Explorer is the king of leakers.
Don't get me wrong. I do not belong to the crowd yelling "Hey IE has memory leaks, checkout this new tool [link-to-tool] and see for yourself". Let us discuss how crappy Internet Explorer is and cover up all the flaws in other browsers".
Each browser has its own strengths and weaknesses. For instance, Mozilla consumes too much of memory at initial boot, it is not good in string and array operations; Opera may crash if you write a ridiculously complex DHTML script which confuses its rendering engine.
If you have read my previous article, then you already know that I am a usability, accessibility and standards crack. I love Opera. But that does not mean I am pursuing a holy war against IE. What I want here is to follow up an analytical path and examine various leaking patterns that may not be quite obvious at first glance.
Although we will be focusing on the memory leaking situations in Internet Explorer, this discussion is equally applicable to other browsers.
A simple beginning
Let us begin with a simple example:
[Exhibit 1 - Memory leaking insert due to inline script] <html> <head> <script type="text/javascript"> function LeakMemory(){ var parentDiv = document.createElement("<div onclick='foo()'>"); parentDiv.bigString = new Array(1000).join( new Array(2000).join("XXXXX")); } </script> </head> <body> <input type="button" value="Memory Leaking Insert" onclick="LeakMemory()" /> </body> </html>
The first assignment parentDiv=document.createElement(...);
will create a div
element and create a temporary scope for it where the scripting object resides. The second assignment parentDiv.bigString=...
attaches a large object to parentDiv
. When LeakMemory()
method is called, a DOM element will be created within the scope of this function, a very large object will be attached to it as a member property and the DOM element will be de-allocated and removed from memory as soon as the function exits, since it is an object created within the local scope of the function.
When you run the example and click the button a few times, your memory graph will probably look like this:
Increasing the frequency
No visible leak huh? What if we do this a few hundred times instead of twenty, or a few thousand times? Will it be the same? The following code calls the assignment over and over again to accomplish this goal:
[Exhibit 2 - Memory leaking insert (frequency increased) ] <html> <head> <script type="text/javascript"> function LeakMemory(){ for(i = 0; i < 5000; i++){ var parentDiv = document.createElement("<div onClick='foo()'>"); } } </script> </head> <body> <input type="button" value="Memory Leaking Insert" onclick="LeakMemory()" /> </body> </html>
And here follows the corresponding graph:
The ramp in the memory usage indicates leak in memory. The horizontal line (the last 20 seconds) at the end of the ramp is the memory after refreshing the page and loading another (about:blank) page. This shows that the leak is an actual leak and not a pseudo leak. The memory will not be reclaimed unless the browser window and other dependant windows if any are closed.
Assume you have a dozen pages that have similar leakage graph. After a few hours, you may want to restart your browser (or even your PC) because it just stops responding. The naughty browser is eating up all your resources. However, this is an extreme case because Windows will increase the virtual memory size as soon as your memory consumption reaches a certain level.
This is not a pretty scenario. Your client/boss will not be very happy, if they discover such a situation in the middle of a product showcase/training/demo.
A careful eye may have caught that there is no bigString in the second example. This means that the leak is merely because of the internal scripting object (i.e. the anonymous script onclick='foo()'
). This script was not deallocated properly. This caused memory leak at each iteration. To prove our thesis let us run a slightly different test case:
[Exhibit 3 - Leak test without inline script attached] <html> <head> <script type="text/javascript"> function LeakMemory(){ for(i = 0; i < 50000; i++){ var parentDiv = document.createElement("div"); } } </script> </head> <body> <input type="button" value="Memory Leaking Insert" onclick="LeakMemory()" /> </body> </html>
And here follows the corresponding memory graph:
As you can see, we have done fifty thousand iterations instead of 5000, and still the memory usage is flat (i.e. no leaks). The slight ramp is due to some other process in my PC.
Let us change our code in a more standard and somewhat unobtrusive manner (not the correct term here, but can't find a better one) without embedded inline scripts and re-test it.
Introducing the closure
Here is another piece of code. Instead of appending the script inline, we attach it externally:
[Exhibit 4 - Leak test with a closure] <html> <head> <script type="text/javascript"> function LeakMemory(){ var parentDiv = document.createElement("div"); parentDiv.onclick=function(){ foo(); }; parentDiv.bigString = new Array(1000).join(new Array(2000).join("XXXXX")); } </script> </head> <body> <input type="button" value="Memory Leaking Insert" onclick="LeakMemory()" /> </body> </html>
If you don't know what a closure is, there are very good references on the web where you may find it. Closures are very useful patterns; you should learn them and keep them in your knowledge base.
And here is the graph that shows the memory leak. This is somewhat different from the former examples. The anonymous function assigned to parentDiv.onclick
is a closure that closes over parentDiv
, which creates a circular reference between the JS world and DOM and creates a well-known memory leakage issue:
To generate leak in the above scenario, we should click the button, refresh the page, click the button again, refresh the page and so on.
Clicking the button without a subsequent refresh will generate the leak only once. Because, at each click, the onclick
event of parentDiv
is reassigned and the circular reference over the former closure is broken. Hence at each page load there is only one closure that cannot be garbage collected due to circular reference. The rest is successfully cleaned up.
More leakage patterns
All of the patterns shown below are described in detail in Justing's article. I'm going through them just for the sake of completeness:
[Exhibit 5 - Circular reference because of expando property] <html> <head> <script type="text/javascript"> var myGlobalObject; function SetupLeak(){ //Here a reference created from the JS World //to the DOM world. myGlobalObject=document.getElementById("LeakedDiv"); //Here DOM refers back to JS World; //hence a circular reference. //The memory will leak if not handled properly. document.getElementById("LeakedDiv").expandoProperty= myGlobalObject; } </script> </head> <body onload="SetupLeak()"> <div id="LeakedDiv"></div> </body> </html>
Here the global variable myGlobalObject
refers to the DOM element LeakDiv
; at the same time LeakDiv
refers to the global object through its expandoProperty
. The situation looks like this:
The above pattern will leak due to the circular reference created between a DOM node and a JS element.
Since the JScript garbage collector is a mark and sweep GC, you may think that it would handle circular references. And in fact it does. However this circular reference is between the DOM and JS worlds. DOM and JS have separate garbage collectors. Therefore they cannot clean up memory in situations like the above.
Another way to create a circular reference is to encapsulate the DOM element as a property of a global object:
[Exhibit 6 - Circular reference using an Encapsulator pattern] <html> <head> <script type="text/javascript"> function Encapsulator(element){ //Assign our memeber this.elementReference = element; // Makea circular reference element.expandoProperty = this; } function SetupLeak() { //This leaks new Encapsulator(document.getElementById("LeakedDiv")); } </script> </head> <body onload="SetupLeak()"> <div id="LeakedDiv"></div> </body> </html>
Here is how it looks like:
However, the most common usage of closures over DOM nodes is event attachment. The following code will leak:
[Exhibit 7 - Adding an event listener as a closure function] <html> <head> <script type="text/javascript"> window.onload=function(){ // obj will be gc'ed as soon as // it goes out of scope therefore no leak. var obj = document.getElementById("element"); // this creates a closure over "element" // and will leak if not handled properly. obj.onclick=function(evt){ ... logic ... }; }; </script> </head> <body> <div id="element"></div> </body> </html>
Here is a diagram describing the closure which creates a circular reference between the DOM world and the JS world.
The above pattern will leak due to closure. Here the closure's global variable obj
is referring to the DOM element. In the mean time, the DOM element holds a reference to the entire closure. This generates a circular reference between the DOM and the JS worlds. That is the cause of leakage.
When we remove closure we see that the leak has gone:
[Exhibit 8- Leak free event registration - No closures were harmed] <html> <head> <script type="text/javascript"> window.onload=function(){ // obj will be gc'ed as soon as // it goes out of scope therefore no leak. var obj = document.getElementById("element"); obj.onclick=element_click; }; //HTML DOM object "element" refers to this function //externally function element_click(evt){ ... logic ... } </script> </head> <body> <div id="element"></div> </body> </html>
Here is the diagram for the above code piece:
This pattern will not leak because as soon as the function window.onload
finishes execution, the JS object obj
will be marked for garbage collection. So there won't be any reference to the DOM node on the JS side.
And the last but not the least leak pattern is the "cross-page leak":
[Exhibit 10 - Cross Page Leak] <html> <head> <script type="text/javascript"> function LeakMemory(){ var hostElement = document.getElementById("hostElement"); // Do it a lot, look at Task Manager for memory response for(i = 0; i < 5000; i++){ var parentDiv = document.createElement("<div onClick='foo()'>"); var childDiv = document.createElement("<div onClick='foo()'>"); // This will leak a temporary object parentDiv.appendChild(childDiv); hostElement.appendChild(parentDiv); hostElement.removeChild(parentDiv); parentDiv.removeChild(childDiv); parentDiv = null; childDiv = null; } hostElement = null; } </script> </head> <body> <input type="button" value="Memory Leaking Insert" onclick="LeakMemory()" /> <div id="hostElement"></div> </body> </html>
Since we observe memory leakage even in Exhibit 1, it is not surprising that this pattern leaks. Here is what happens: When we append childDiv
to parentDiv
, a temporary scope from childDiv
to parentDiv
is created which will leak a temporary script object. Note that document.createElement("<div onClick='foo()'>");
is a non-standard method of event attachment.
Simply using the "best practices" is not enough (as Justing has mentioned in his article as well). One should also adhere to standards as much as possible. If not, he may not have a single clue about what went wrong with the code that was working perfectly a few hours ago (which had just crashed unexpectedly).
Anyway, let us re-order our insertion. The code below will not leak:
[Exhibit 11 - DOM insertion re-ordered - no leaks] <html> <head> <script type="text/javascript"> function LeakMemory(){ var hostElement = document.getElementById("hostElement"); // Do it a lot, look at Task Manager for memory response for(i = 0; i < 5000; i++){ var parentDiv = document.createElement("<div onClick='foo()'>"); var childDiv = document.createElement("<div onClick='foo()'>"); hostElement.appendChild(parentDiv); parentDiv.appendChild(childDiv); parentDiv.removeChild(childDiv); hostElement.removeChild(parentDiv); parentDiv = null; childDiv = null; } hostElement = null; } </script> </head> <body> <input type="button" value="Memory Leaking Insert" onclick="LeakMemory()" /> <div id="hostElement"></div> </body> </html>
We should keep in mind that, although it is the market leader, IE is not the only browser in the world. And writing IE-specific non-standard code is a bad practice of coding. The counter-argument is true as well. I mean, saying "Mozilla is the best browser so I write Mozila-specific code; I don't care what the heck happens to the rest" is an equally bad attitude. You should enlarge your spectrum as much as possible. As a corollary, you should write standards-compatible code to the highest extent, whenever possible.
Writing, "backwards compatible" code is "out" nowadays. The "in" is writing "forward compatible" (also known as standards compatible) code which will run now and in the future, in current and in future browsers, here and on the moon.
Conclusion
The purpose of this article was to show that not all leakage patterns are easy to find. You may hardly notice some of them, may be due to the small bookkeeping objects that become obvious only after several thousands of iterations. Knowing the internals of a process is an undeniable key to success. Be aware of the leak patterns. Instead of debugging your application in a brute-force manner, look at your code fragments and check whether there is any piece that matches a leakage pattern in your arsenal.
Writing defensive code and taking care of all possible leakage issues is not an over-optimization. To take it simpler, leak-proofness is not a feature the developer may choose to implement or not depending on his mood. It is a requirement for creating a stable, consistent and forward-compatible code. Every web developer should know about it. No excuses, sorry. There are several solutions proposed for leakage issues between a JS closure and a DOM object. Here are a few of them:
- Leak free JavaScript closures
- Chroder's Event Manager
- Dean Edward's addEvent method
- My humble EventHandler object -which will change a little, because my entire API is under a revision.
However, you should be aware of the fact that there are always unique cases where you may need to craft a solution for yourself. That's it for now. Although the article is not intended to be the "best practices in avoiding memory leakage", I hope it has pointed out some of the interesting issues.
Happy coding!
History
- 2005-11-12: Article created.
About volkan.ozcelik
Volkan is a java enterprise architect who left his full-time senior developer position to venture his ideas and dreams. He codes C# as a hobby, trying to combine the .Net concept with his Java and J2EE know-how. He also works as a freelance web application developer/designer. Volkan is especially interested in database oriented content management systems, web design and development, web standards, usability and accessibility. He was born on May '79. He has graduated from one of the most reputable universities of his country (i.e. Bogazici University) in 2003 as a Communication Engineer. He also has earned his Master of Business Administration degree from a second university in 2006. Click here to view volkan.ozcelik's online profile. |
Other popular JavaScript articles:
|
评论
原文链接
发表评论
-
js正则表达式zz
2007-09-20 10:36 2069<%-- js正则表达式 2007- ... -
如何阻止冒泡(zz)
2007-09-18 14:54 2497AJAX ... -
受信站点设置
2007-09-14 14:40 1567除了添加localhost到本地 还要把 安全级别里面的 通过 ... -
ajax应用的基本流程zz
2007-09-03 00:24 1894ajax应用的基本流程 1、从web表单中获取需要的数据 2 ... -
Jsp中使用xmlhttp进行数据交互zz
2007-09-02 23:55 10031.客户端提交请求 Domain = " te ... -
JSP中自定义标记符的使用zz
2007-09-02 23:52 1301JSP ... -
weblogic培训笔记 zz
2007-08-30 08:41 3194weblogic培训笔记 [ ... -
Aptana使用之ecilpse monkey技巧
2007-08-26 22:22 5098首次原创,谢谢大家支持 现在很多ajax程序员使用Aptan ... -
Myeclipse中启动Weblogic9异常解决 zz
2007-08-24 17:49 6467Myeclipse中启动Weblogic9异常解决 出现这 ... -
所有weblogic下载
2007-08-24 11:31 6210所有weblogic版本下载 ... -
诡异的Tomcat
2007-08-21 17:32 1107下午陪个公司的应用网站系统 老是报org.apache.jas ... -
把外部文件配置成tomcat站点zz
2007-08-21 15:25 1594假设有D:\RegWeb站点,在Tomcat 5.0以上版本如 ...
相关推荐
本书《LEAKAGE IN NANOMETER CMOS TECHNOLOGIES》深入浅出地探讨了纳米CMOS技术中的漏电流问题,并介绍了一些常见的漏电流机制和解决方法。书中还讨论了漏电流对电路的影响,以及如何通过设计和技术创新来减少漏电流...
本压缩包文件“DATA-LEAKAGE-DETECTION-using-asp.net.zip_data leakage”显然是关于如何在ASP.NET环境中实施数据泄露检测的教程或演示。 数据泄露检测系统的主要目标是预防、检测和应对潜在的数据泄露事件,确保...
"藏经阁-Skype-&-Type-Keystroke-Leakage-Over-VoIP-30.pdf" 本资源主要介绍了通过VoIP进行键盘击键泄露攻击的方法,这是一种新的攻击方式,通过监听Skype语音通话中的键盘击键声音来获取用户的键盘输入信息。 ...
### Memory Leakage & Initialization #### 一、如何避免编程中的内存泄漏? 内存泄漏是程序设计中一个常见的问题,尤其是在使用 C 语言这样的低级语言时。内存泄漏不仅会消耗宝贵的资源,还可能导致程序运行不...
the possible key leakage, leakage-resilient cryptography models a class of leakage output by allowing<br /> the adversary to be able to specify a computable leakage function and obtaining the ...
《IEICE-leakage-review-journal.pdf》这篇文章主要探讨了在CMOS VLSI电路中如何控制和最小化待机及活动状态下的漏电流问题。随着半导体技术的快速发展,芯片密度和工作频率的提高,电力消耗成为了便携式设备的一大...
### 常见错误分析:内存泄漏与无效指针 #### 如何避免编程中的内存泄漏? 内存泄漏是指程序在申请动态内存后未能正确释放,导致系统资源被不断占用的现象。长期运行的应用程序若存在内存泄漏,则可能会导致系统...
发表在NDSS‘16上的论文 The Price of Free:Privacy Leakage in Personalized Mobile In-App Ads。这篇文章研究了Android平台上广告库的隐私泄露问题。 Introduction 作者分析了Android平台上广告库的隐私泄露问题,...
The free-swimming device leakage detection in plastic water-filled pipes through tuning the wavelet transform to the underwater acoustic signals
system to load, or transfer full-length high-definition movie by memory stick in seconds rather than hours. Such a life would happen if a universal nonvolatile memory could be developed that not only ...
本文讨论了协作机器学习中未预期特征泄露问题,特别是联邦学习环境下的隐私泄露风险。协作学习允许多个拥有各自训练数据集的参与者,通过在本地训练模型并定期交换模型更新来共同构建一个联合模型。...
We propose a novel model to explain the physical process of the thermally induced core laser leakage (TICLL) effect in a high power co-pumped ytterbium doped fiber (YDF) amplifier. This model ...
How to Avoid Memory Leakage in Your Programming? Memory leaks occur when a program allocates memory but fails to release it back to the system, causing the application to consume more and more ...
标题 "Do all screw dislocations cause leakage in GaN-based devices?" 提出的问题是关于在氮化镓(GaN)基器件中,所有螺旋位错是否都会导致漏电流的问题。这是一个重要的研究课题,因为位错在半导体器件中是常见...
### 泄漏功率降低通过多阈值电压单元交换技术 #### 概述 在现代集成电路设计中,泄漏功率已经成为一个非常关键的问题。随着工艺技术的进步,晶体管尺寸不断缩小,导致泄漏电流增加,进而增加了静态功耗。...
该文章主要探讨了一类具有Leakage时滞的惯性Cohen-Grossberg神经网络的全局指数稳定性和Hopf分支。Cohen-Grossberg神经网络是一种广泛应用在神经科学和计算模型中的理论框架,用于模拟神经元之间的交互作用。在本文...
改进的深度泄漏 “”(iDLG)的代码。 抽象的 人们普遍认为,共享梯度不会泄漏诸如协作学习和联合学习等分布式学习系统中的私人训练数据。 [1]提出了一种方法,该方法显示了从公开共享的梯度中获得私人训练数据的...
"前端开源库-leakage" 是一个专门用于检测和测试JavaScript中内存泄漏的工具。它旨在帮助开发者确保他们的应用程序在运行过程中不会因为内存泄漏问题而降低性能或导致意外的崩溃。 内存泄漏是指程序在申请内存后,...
lpc matlab代码本文的研究数据和模型描述 支持该研究结果的部分或全部数据,模型或代码可应合理要求从相应的作者处获得。 在这里,我们介绍主要的型号代码。 名为“ LPC.py”的文件与使用原始数据集的基于LCP的检测...
handling of packaged download - ----- 3.1 intermediate process tracking and output variables ------ 3.2 / Object legitimacy inspection 3.3 ------ ------ memory leakage inspection 3.4 abnormal capture