- 浏览: 394378 次
- 性别:
- 来自: dazhou
最新评论
-
coosummer:
推荐使用http://buttoncssgenerator.c ...
几个漂亮的Button的CSS -
wyzxzws:
<div class="quote_title ...
js前端之---内存泄漏 -
yoven:
那如何检测程序中是否存在内存泄漏呢?
js前端之---内存泄漏 -
asigh:
麻烦博主给下整个mxml文件的代码,1104858212@qq ...
Flex弹出框添加IFrame,jsp或者html页面不随弹出框一起移动问题 -
greatghoul:
http://greatghoul.iteye.com/blo ...
jfreechart饼图
转自:http://www.blogjava.net/JAVA-HE/archive/2009/10/27/299856.html
问题:js前端之---内存泄漏
解决方案:
JavaScript 内存泄露
今天下午同事让帮忙看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 onclick="LeakMemory()">Memory Leaking Insert</button> <button onclick="CleanMemory()">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仍然有内存泄露
评论
http://www.php100.com/html/webkaifa/javascript/2012/0504/10356.html
发表评论
-
可以查看远程库的一些信息,及与本地分支的信息。有时候可能遇到如下情况
2017-10-12 10:35 451可以查看远程库的一些信息,及与本地分支的信息。有时候 ... -
删除除了master的分支
2017-10-12 10:34 895删除本地除了master的分支 git br ... -
charles抓不到包问题
2016-07-20 17:07 3126http://l23q5.com/blog/2016/03/2 ... -
js Date问题
2015-11-06 18:23 619javascript中Date对象实例化差别问题: ... -
mac sed目录文件内容替换
2015-09-07 16:22 1015需求:把当前目录下所有js文件里的“URL”替换成“GET ... -
vim配置方案
2015-09-06 17:25 758最近在学习vim做前端开发,通过各种大神的经验和我的教训得出 ... -
vim 系统剪贴板负责粘贴
2015-08-27 17:23 946在vimrc里边加入下面几行,可以实现多个termi ... -
osx查找复制文件
2015-08-26 16:25 489sudo find ./ -name '*.jpg' -e ... -
css三角
2015-08-07 16:27 451<!DOCTYPE html> <htm ... -
微信开发授权文档
2015-03-30 10:29 748微信授权开发: 微信JS SDK说明文档 ... -
MAC man命令的中文帮助文档
2015-03-23 15:01 1313mac系统版本: OSX 10.8.2 默认在终端进行ma ... -
nginx配置mac-403
2015-03-20 10:31 842在 MAC 上用BREW 安装好NGINX 后,修改网站文 ... -
配色理论
2014-12-17 10:05 976在设计领域,颜色是 ... -
html页面调用安卓应用
2014-12-16 16:56 738今天老大给我提了一个需求叫我调研一下,他已经测试了IOS平 ... -
css布局上中下,左中右
2014-12-12 17:05 634<!DOCTYPE html> <htm ... -
汉字转拼音处理
2014-12-11 16:47 1817汉字转拼音处理: pym: { ... -
ps奥运五环
2014-12-11 16:33 790ps奥运五环 -
ps公章制作
2014-12-11 13:40 864ps公章制作,练习PS -
acdsee15破解方法
2014-12-09 10:28 562开始---运行---输入:regedit 进入注册表 ... -
ie6绝对居中彻底解决
2014-12-04 14:57 691代码: css .center { posi ...
相关推荐
3. 在 dev-runner.js 中找到了关于杀死进程的方法,使用 process.kill(electronProcess.pid) 杀死进程,以解决内存泄漏问题。 解决思路: 使用 process.kill(electronProcess.pid) 杀死进程,以解决内存泄漏问题。...
`ssh2-sftp-client`是这样一个专门为前端开发者设计的开源库,它使得在Node.js环境中通过SFTP(Secure File Transfer Protocol)协议与远程服务器进行安全的文件操作变得更加便捷。 SFTP是基于SSH协议的文件传输...
前端内存泄漏及解决方案详解 在前端开发中,内存泄漏是一个非常重要的主题。内存泄漏是指系统进程不再使用的内存没有及时释放,导致内存占用越来越高,轻则影响系统性能,重则导致进程崩溃。在本文中,我们将讨论...
9. 性能优化:避免内存泄漏、减少DOM操作、使用事件委托等。 这套参考手册为前端开发者提供了详尽的指导,涵盖了从基础概念到高级技术的广泛内容,是学习和工作中不可或缺的参考资料。通过深入理解和熟练掌握这些...
这种情况下,如果不再有其他引用指向这些对象,它们将形成一个循环链,导致内存泄漏。is-circular库可以帮助开发者识别并解决这类问题,避免潜在的性能瓶颈和内存占用过多的情况。 2. 高性能: 对于处理大量数据的...
在开发过程中,开发者需要关注性能优化,如懒加载数据、避免内存泄漏,以及确保在不同浏览器上的兼容性。此外,jExcel还支持插件扩展,可以利用这一特性来增加更多的功能,如图表绘制、数据验证等。 总之,"前端...
在前端开发领域,事件驱动编程模式是构建交互性强、响应迅速的应用程序的关键技术之一。"前端项目-EventEmitter.zip"是一个专注于浏览器环境下的事件处理库,它实现了EventEmitter模式,使得JavaScript代码可以通过...
3. **事件解绑**:为了防止内存泄漏,需要在不再需要监听事件时解除绑定。`off`方法可以移除特定事件的监听器,或者全部移除。例如,`this.stopListening()`会清除当前对象的所有监听。 4. **事件委托**:在...
5. **清理**:为了防止内存泄漏,完成数据处理后,需要删除创建的`<script>`标签。 Fetch API是一个现代的、更强大且易于使用的替代XMLHttpRequest的接口,它遵循Promise规范,提供了一种更简洁的异步编程方式。在...
- **性能优化**:合理使用数据绑定和事件监听,避免内存泄漏和不必要的DOM操作。 - **版本控制**:使用Git等版本控制系统,确保代码安全和团队协作。 - **测试**:编写单元测试和集成测试,确保代码的健壮性和...
JavaScript是一种广泛应用于Web开发的脚本语言,它在创建交互式和动态网页方面发挥了重要作用。然而,由于其异步执行模型和垃圾回收...同时,了解和掌握JavaScript的内存管理机制也是每个前端开发者必备的技能之一。
JS引擎通过垃圾回收自动管理内存,了解其工作原理,如标记清除、引用计数等,可以帮助避免内存泄漏和提高性能。 7. **异步编程**: 异步编程是JavaScript的一大特点,掌握Promise、async/await等处理异步的方法,...
【标签】:“前端项目”表明这是一个用于构建前端Web应用程序的工具,利用JavaScript库Backbone.js来实现。 【压缩包子文件的文件名称列表】:“backbone.babysitter-master”很可能包含了该项目的源代码、示例、...
5. **资源管理**:合理管理Worker的生命周期,避免内存泄漏和其他性能问题。 总的来说,Catiline是一个面向前端开发者的强大工具,它通过引入多进程处理,使得前端应用能够处理更复杂的任务,同时保持界面的流畅和...
使用Processing.js时,要注意性能优化,避免不必要的重绘和内存泄漏。合理使用`requestAnimationFrame`进行动画更新,以及及时清理不再使用的对象,都是提高用户体验的关键。 综上所述,Processing.js是一个强大的...
- 对于不支持该事件的老版IE,记得清除定时器,以防止内存泄漏。 - 为了确保最佳用户体验,应当合理设置定时器的检测间隔,既不过于频繁,也不过慢。 总的来说,jQuery HashChange插件是实现前端项目中哈希值监听和...
React 是一个流行的前端框架,但是它也存在一些常见的问题,其中之一就是内存泄露。在本文中,我们将讨论什么是内存泄露,React 中常见的内存泄露的情况,以及如何解决这些问题。 什么是内存泄露? ---------------...
JS内存泄漏是前端开发过程中经常遇到的问题,它指的是程序中已分配的内存由于某些原因未被释放或者无法释放,导致应用程序可用的内存逐渐减少。本节将详细探讨JS内存泄漏的原因、检测方法以及如何预防和解决内存泄漏...
6. **解除绑定**:当不再需要绑定的数据时,记得使用`this.$unbind`方法解除绑定,以避免内存泄漏。 在“vuefire-master”这个压缩包中,可能包含了以下内容: - `src`: 源代码目录,包括Vue组件、样式文件和脚本。...
- **性能优化**:内存泄漏检测,电量优化,APK瘦身策略。 3. **前端知识**: - **HTML/CSS/JavaScript**:基础语法,响应式布局,事件处理,跨域解决方案。 - **框架与库**:React、Vue、Angular等主流框架的...