- 浏览: 239242 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
thepastsee:
304572183股票交流 欢迎加入
实时股票 -
345161974:
很不错,我第一个接触的CSS卡片布局效果,多谢
css卡片效果
】JavaScript 内存泄露
文章分类:Web前端
Javascript的内存泄漏,不是太可怕。它只会悄悄的,慢慢的把你的浏览器拖的巨慢无比,让你愤怒的拍案而起,大骂微软出品的破烂浏览器危害社会。这一切有可能并不是浏览器的错,可能只是因为网页
上有些javascript
的内存泄漏罢了。
在科技日益发达今天,我们有必要武装自己,以及自己的浏览器,这样万一浏览器倒下了,还能知道到底是死在谁家的网页上面。下面这个Firefox插件是推荐给那些UI设计者或者开发
人员的:Leak Monitor
This extension pops up an alert dialog to warn chromeand extension developers about one particular type of leak. It warnswhen chrome windows close but leave other code pointing at theirJavaScript objects.
Works with:
Firefox 1.5 – 3.0 ALL
Thunderbird 1.5 – 3.0 ALL
在你访问一张网页的时候,如果有javascript内存泄漏,这个extension就会啪的一下给你弹出一张内存泄漏的清单。就拿现在用的
wordpress后台来说吧,Leak Monitor也是毫不客气的给弹了一个窗口,主要是因为使用了大名鼎鼎的prototype.js…下载
https://addons.mozilla.org/firefox/2490/
今天下午同事让帮忙看web内存泄露问题。当时定位到创建ActiveX 对象的时候产生的,于是我对这个奇怪的问题进行了一些深入探索。
很多时候我都依赖javascript的垃圾回收机制,所以对C 以及C++ 操作内存语言常发生的内存泄露是很陌生的。当时创建回调函数用了闭包,当然最终的解决方法是也避免闭包调用。
随着这个问题的浮出水面,我回忆起以前的一个项目中也应该存在这个内存泄露问题。于是查阅了相关资料把类似的问题总结下来,希望对大家也有帮助。
原因:对于一门具有垃圾收回机制的语言存在内存泄露,其原因不外乎就是javascript脚本引擎存在bug。
很多时候,我们要做的不是去修正那样的bug,而是想办法去规避。
目前发现的可能导致内存泄露的代码 有三种:
· 循环引用
· 自动类型装箱转换
· 某些DOM操作
下面具体的来说说内存是如何泄露的
循环引用:这种方式存在于IE6和FF2中(FF3未做测试),当出现了一个含有DOM对象的循环引用时,就会发生内存泄露。
什 么是循环引用?首先搞清楚什么是引用,一个对象A的属性被赋值为另一个对象B时,则可以称A引用了B。假如B也引用了A,那么A和B之间构成了循环引用。 同样道理 如果能找到A引用B B引用C C又引用A这样一组饮用关系,那么这三个对象构成了循环引用。当一个对象引用自己时,它自己形成了循环引用。注意,在js中变量永远是对象的属性,它可以 指向对象,但决不是对象本身。
循环引用很常见,而且通常是无害的,但如果循环引用中包含DOM对象或者ActiveX对象,那么就会发生内存泄露。例子:
var a=document.createElement("div");
var b=new Object();
a.b=b;
b.a=a;
很多情况下循环引用不是这样的明显,下面就是著名的闭包(closure)造成内存泄露的例子,每执行一次函数A()都会产生内存泄露。试试看,根据前面讲的scope对象的知识,能不能找出循环引用?
function A()...{
var a=document.createElement("div");
a.onclick=function()...{
alert("hi");
}
}
A();
OK, 让我们来看看。假设A()执行时创建的作用域对象叫做ScopeA 找到以下引用关系
ScopeA引用DOM对象document.createElement("div");
DOM对象document.createElement("div");引用函数function(){alert("hi")}
函数function(){alert("hi")}引用ScopeA
这样就很清楚了,所谓closure泄露,只不过是几个js特殊对象的循环引用而已。
自动类型装箱转换:这种泄露存在于ie6 ie7中。这是极其匪夷所思的一个bug,看下面代码
var s="lalalalala";
alert(s.length);
这 段代码怎么了?看看吧,"lalalalala"已经泄露了。关键问题出在s.length上,我们知道js的类型中,string并非对象,但可以对它 使用.运算符,为什么呢?因为js的默认类型转换机制,允许js在遇到.运算符时自动将string转换为object型中对应的String对象。而这 个转换成的临时对象100%会泄露(汗一下)。
某些DOM操作也可能导致泄露 这些恶心的bug只存在于ie系列中。在ie7中 因为试图fix循环引用bug而让情况变得更糟,以至于我对写这一段种满了恐惧。
从ie6谈起,下面是微软的例子,
<html>
<head>
<script language="JScript">...
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;
}
function CleanMemory()
...{
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()'>");
// Changing the order is important, this won't leak
hostElement.appendChild(parentDiv);
parentDiv.appendChild(childDiv);
hostElement.removeChild(parentDiv);
parentDiv.removeChild(childDiv);
parentDiv = null;
childDiv = null;
}
hostElement = null;
}
</script>
</head>
<body>
<button>Memory Leaking Insert</button>
<button>Clean Insert</button>
<div id="hostElement"></div>
</body>
</html>
看 看结果吧,LeakMemory造成了内存泄露,而CleanMemory没有,循环引用了么?仔细看看没有。那么是什么问题呢?MS的解释是"插入顺序 不对",必须先将父级元素appendChild。这听起来有些模糊,这里给出一个比较恰当的等价描述:永远不要使用DOM节点树之外元素的 appendChild方法。
我 曾经看到过这样的说法,创建dom的时候,先创建子节点,当子节点完善后一次性添加到页面中,不要一点点朝页面上加东西,尽量减少document刷新次 数,这样效率会高点。(打个比方就是应该像 LeakMemory )可见这里我还是被某些书籍误导了。至少他没有告诉我内存泄露的问题。
接下来是ie7和ie8 beta 1中运行这段程序 , 看到什么?没看错吧,2个都泄露了!别急,刷新一下页面就好了。为什么呢?ie7改变了DOM元素的回收方式:在离开页面时回收DOM树上的所有元素,所 以ie7下的内存管理非常简单:在所有的页面中只要挂在DOM树上的元素,就不会泄露,没挂在DOM树上,肯定泄露。所以,ie7中记住一条原则:在离开 页面之前把所有创建的DOM元素挂到DOM树上。
接 下来谈谈ie7的这个设计吧,坦白的说,这种做法纯粹是偷懒的垃圾做法。动态垃圾回收不是保证所有内存都在离开页面时收回,而是要保证内存的充分利用,运 行时不回收,等到离开时回收有什么用?这只是名义上的避免泄露,其实是完全的泄露。况且还没有回收DOM节点树之外的元素。
4.内存泄露的解决方案
内存泄露怎么办?真的以后不用闭包了么?没法封装控件了?这样做还不如要了js程序员 的命,嘿嘿。
事实上,通过一些很简单的小技巧,可以巧妙的绕开这些危险的bug。
to be continued......
coming soon:
· 显式类型转换
· 避免事件导致的循环引用
· 不影响返回值地打破循环引用
· 延迟appendChild
· 代理DOM对象
· 显式类型转换
首先说说最容易处理的情况 对于类型转换造成的错误,我们可以通过显式类型转换来避免:
var s=newString("lalalalala");//此处将string转换成object
alert(s.length);
这个太容易了,算不上正经方案。不过类型转换泄露也就这一种处理方法了。
· 避免事件导致的循环引用
在比较成熟的js程序员里,把事件函数写成闭包是再正常不过了:
function A(){
var a=document.createElement("div");
a.onclick=function(){
alert("hi");
}
}
这将导致内存泄露。按照IBM那两位老大的说法,当然是把函数放外面或者a=null就没问题了,不过还要访问A()里面的变量呢?假如有下面的代码:
function A(){
var a=document.createElement("div");
var b=document.createElement("div");
a.onclick=function(){
alert(b.outerHTML);
}
return a;
}
如何将它的逻辑表达出来 还避免内存泄露? 分析一下这个内存泄露的形式:只要onclick的外部环境中不包含a那么,就不会泄露。那么办法有2个一是将环境到a的引用断开 另一个是将function到环境的引用断开,但是,如果要在函数中访问b就不能将Function放到外面,如果要返回a的值,就不能a=null,怎 么办呢?
解决方案1:
构造一个不含a的新环境
function A(){
var a=document.createElement("div");
var b=document.createElement("div");
a.onclick=BuildEvent(b);
return a;
}
function BuildEvent(b)
{
return function(){
alert(b.outerHTML);
}
}
a本身可以通过this访问,将其它需要访问的外层函数变量传递给BuildEvent就可以了。保持BuildEvent定义和调用的参数名一致,会带来方便。
解决方案2:
在return 之后a=null,不可能? 看看下面:
function A(){
try{
var a=document.createElement("div");
var b=document.createElement("div");
a.onclick= function(){
alert(b.outerHTML);
}
return a;
} finally {
a=null;
}
}
finally在try之后执行,如果finall块不返回值,才会返回try块的返回值。
· 延迟appendChild
还 记得函数的lazy initalize吧,对于ie恶心至极的DOM操作泄露,我们需要用类似的方法去处理。在一个函数中构造一个复杂对象,在需要的时候将之 appendChild到DOM树上,这是很常见的做法,但在IE6中,这样做将导致所谓的"插入顺序内存泄露",没有别的办法,我们只能用一个数组 parts保存子节点,编写一个appendTo方法先序遍历节点树,去把它挂在某个DOM节点上。
function appendTo(Element)
...{
Element.appendChild(this);
if(!this.parts)return;
for(var i=0;i<this.parts.length;i++)
parts.appendTo(this);
}
· 垃圾箱
对于ie7,我比较无可奈何,因为DOM对象不会被CG程序回收,只有离开页面时会被回收,所以我的建议是:使用DOM要有节制,尽量多用innerHTML吧...... good luck.
一旦你使用了DOM对象,千万不要试图o=null,你可以设置一个叫做Garbage的div并且将其display设置为none,将不用的DOM对象存入其中(就是appendChild上去)就好了
· 代理对象
这 是Ext的做法,这里只是顺带提一下。将每个元素用一个"代理对象"操作,不论appendChild还是其他操作都不是对DOM对象本身的操作,而是通 过这个代理对象操作。这是一个很不错的Proxy模式,不过要想避免泄露还是需要一点功夫的,并非用了Proxy之后就不会泄露,有时反而更容易泄露。
5 .FAQ
1 内存泄露是内存占用很大么? 不是,即使1byte内存也叫做内存泄露。
2 程序中提示,内存不足,是内存泄露么?不是,这一般是无限递归函数调用导致栈内存溢出。
3 内存泄露是哪个区域泄露?堆区,栈区是不会泄露的。
4 window对象是DOM对象么?不是,window对象参与的循环引用不会内存泄露。
5 内存泄露后果是什么?大多数时候后果不很严重,但过多DOM操作会导致网页执行变慢。
6 跳转页面后,内存泄露仍然存在么?仍然存在,直到关闭浏览器。
7 FireFox也会内存泄露么?FF2仍然有内存泄露
其他与js内存泄漏相关的文章:http://www.ibm.com/developerworks/cn/web/wa-memleak/index.html
发表评论
-
jquery_easyui
2012-01-18 10:06 806dfgdfsgs -
俄罗斯方格——转 javaeye
2012-01-16 11:59 825sdfasdfas -
网站模板_down
2012-01-05 14:15 684xcfadfasdf -
命名规则有利于SEO
2011-12-13 14:00 803转转转 命名规则有利于SEO ... -
ff_chm
2011-10-21 12:47 624sadasdasd -
精通javascript 代码
2011-08-24 17:35 736download download -
escape encodeURI 和encodeURIComponent_转
2011-06-21 17:11 810http://blog.csdn.net/redbednil/ ... -
javascript 讲义_01_事件结束
2011-06-19 09:50 622iiui -
js 验证非常好_转
2011-06-12 08:47 869http://justshare.iteye.com/blog ... -
jquery_demo_ff_bug_xpi
2011-05-26 12:33 651kjkjkj -
netmeetine_
2011-05-17 17:30 683kljlojlkjlkj -
javascript_简洁验证表单
2011-03-31 19:48 679ljkljk -
fck_精简版
2011-03-20 17:13 1052,jkjm -
css_div_灯箱效果
2011-03-05 16:32 829;lk;lk;lk -
QQ空间_html
2011-02-16 12:40 1133dgdgdg -
javascript_优化
2011-02-14 12:49 821Javascript常用优化方法 ... -
ext_p入门___:)
2011-02-05 12:08 613http://www.iteye.com/topic/1799 ... -
dangdang网_html
2011-01-18 00:07 763大家很努力,结果很不错 :) -
jquery_入门实例
2011-01-07 18:36 563kjlkjkl -
ajax_入门实例_
2011-01-07 18:29 558fwerwer
相关推荐
### JavaScript内存泄露详解 #### 一、什么是JavaScript内存泄露? 在JavaScript编程中,内存泄露指的是在浏览器中不再使用的变量或对象占用的内存没有被及时回收,导致可用内存逐渐减少的现象。这种现象通常发生...
JavaScript内存泄露问题的解析 JavaScript内存泄露问题是一种常见的bug,它会导致系统崩溃和性能下降。内存泄露是指系统不能正确地管理内存分配的情况,这可能会导致程序调用失败、执行减慢等问题。 在JavaScript...
JavaScript内存泄漏是一个重要的编程问题,尤其对于Web应用来说,它可能导致性能下降,用户界面响应变慢,甚至在极端情况下导致应用程序崩溃。理解内存泄漏的原因、如何检测和解决它们是每个JavaScript开发者必备的...
在探讨JavaScript内存泄漏的处理方式前,有必要了解内存泄漏的含义。内存泄漏通常指的是程序不再使用的内存未能被释放,导致内存消耗不断上升。即使在高级语言如JavaScript中,这一问题亦可能出现,尽管它拥有垃圾...
sIEve是一款专门针对JavaScript内存泄漏检测的工具,版本为0.0.8。这款工具的主要目标是帮助开发者识别和定位JavaScript应用中的内存泄漏问题,以优化性能并提高用户体验。sIEve通过深入分析JavaScript运行时的内存...
本文将详细探讨JavaScript内存泄漏的原因,并展示一些常见的内存泄漏模式,以及如何解决这些问题。 首先,JavaScript 是一种垃圾收集语言,意味着对象创建时会分配内存,在没有更多引用时由浏览器回收这些内存。...
JavaScript内存泄漏是一个重要的主题,尤其是在现代Web开发中,因为页面长时间保持活跃且动态更新内容的情况越来越普遍。了解和处理内存泄漏对于优化Web应用性能至关重要。本文将深入探讨JavaScript中的几种常见内存...
【JavaScript源代码】一篇文章弄懂javascript内存泄漏 在JavaScript中,内存管理对于程序性能至关重要,因为内存泄漏会导致程序效率下降,甚至可能导致应用崩溃。本文旨在深入解析JavaScript中的内存泄漏及其解决...
常见的JavaScript内存泄露类型有: 1. 意外的全局变量:未声明的变量默认成为全局变量,这可能导致意外的内存占用。例如,忘记在函数内部使用`var`关键字会导致变量被附加到全局对象window上,形成持久化的内存占用...
### 常见的JavaScript内存泄露原因及解决方案 #### 引言 在现代Web开发中,JavaScript已成为构建复杂用户界面的主要编程语言之一。随着应用程序变得越来越庞大与复杂,有效地管理和优化内存成为确保应用性能和用户...
内存泄漏是程序运行过程中,不再使用的内存没有被正确释放,导致系统资源持续占用,从而影响程序性能甚至系统...了解并掌握这些知识点,开发者可以更有效地预防和解决JavaScript内存泄漏问题,确保应用的高效稳定运行。
2. **内存泄漏类型**:常见的JavaScript内存泄露包括全局变量、闭包引用、DOM元素引用、事件监听器等。了解这些类型有助于识别潜在问题。 3. **工具使用**:利用如IE Developer Tools(F12工具)、Chrome DevTools...
JavaScript内存泄露是一个重要的性能优化话题,尤其是在开发大型的、长时间运行的Web应用时。内存泄露会导致应用程序占用过多的系统资源,影响用户体验,甚至可能导致浏览器崩溃。本文将深入探讨JavaScript内存泄露...
标题中的“脚本IE内存泄露检测.rar”表明这是一个专门针对Internet Explorer浏览器的JavaScript内存泄漏检测工具。内存泄漏是编程中常见的问题,特别是在处理大量数据或长时间运行的网页应用时,可能导致浏览器性能...
“JS关于ie的内存泄漏与javascript内存释放资料.doc”和“Js内存泄漏及解决方案.doc”可能详细解释了JavaScript中如何发生内存泄漏,尤其是在IE浏览器中的特殊情况。IE浏览器因为其独特的内存管理机制,如活动对象链...
总结来说,JavaScript内存泄漏是一个重要问题,它主要与闭包及其引起的循环引用有关。理解JavaScript的垃圾回收机制和闭包的工作原理,有助于我们编写出更安全、更高效的代码,避免不必要的内存泄漏。对于IE浏览器中...
JavaScript内存泄漏指的是由于代码的某些问题导致内存不能被垃圾回收机制正常回收,从而随着时间的推移,内存消耗持续增长。这不仅会导致程序运行缓慢,最终还可能导致程序崩溃。根据给定文件中的内容,我们可以分析...
以下将详细解释几种易导致JavaScript内存泄漏的情况,并涉及如何使用Chrome DevTools进行内存管理与调试。 1. 删除对象属性导致的内存泄漏 在JavaScript中,我们经常使用delete关键字来删除对象的属性,但这种操作...