浅谈 JS 内存泄露方式与避免方法(二)
- Concept
- WHAT :
- 内存泄露是指一块被分配的内存既不能使用,又不能回收,直到浏览器进程结束。正常情况下,垃圾回收器在DOM元素和event处理器不被引用或访问的时候回收它们。但是,IE的早些版本(IE7和之前)中内存泄漏是很容易出现的,因为内存管理器不能正确理解Javascript生命周期而且在周期被打破(可以通过赋值为null实现)前不会回收内存。
- WHY :
- 在大型Web应用程序中内存泄漏是一种常见的意外编程错误。内存泄漏会降低Web应用程序的性能,直到浪费的内存超过了系统所能分配的,应用程序将不能使用。作为一Web开发者,开发一个满足功能要求的应用程序只是第一步,性能要求和Web应用程序的成功是同样重要的,更何况它可能会导致应用程序错误或浏览器崩溃。
- Quick Review
- HOW:
- 不同的浏览器中存在各种内存泄露方式,目前发现的主要是这样几种:
- 循环引用:已经确认存在泄漏的浏览器:IE6.0 FF2.0
- 含有DOM对象的循环引用将导致大部分当前主流浏览器内存泄露 这里有两个简单的概念
- 引用:a.属性=b,a就引用了b
- 循环引用:简单来说假如a引用了b,b又引用了a,a和b就构成了循环引用。
- a和b循环引用:
var a=new Object;
var b=new Object;
a.r=b;
b.r=a; - a循环引用自己:
var a=new Object;
a.r=a;
- 循环引用很常见且大部分情况下是无害的,但当参与循环引用的对象中有DOM对象或者ActiveX对象时,循环引用将导致内存泄露。我们把例子中的任何一个new Object替换成document.getElementById或者document.createElement就会发生内存泄露了。尽管这看起来非常容易理解,但是因为有closure的参与而使事情变得复杂,有些closure导致的循环引用很难被察觉。下面是一个非常常见的动态绑定事件:
function bindEvent() { var obj=document.createElement("XXX"); obj.onclick=function(){ //Even if it's a empty function } } |
- 这个bindEvent执行时100%会发生内存泄露,Someone 可能会问,哪里出现了循环引用? 关于closure和scope chain参与的循环引用比较复杂,此处暂不深入讨论。有一个简单的判断方式:函数将间接引用所有它能访问的对象。obj.onclick这个函数中 可以访问外部的变量obj 所以他引用了obj,而obj又引用了它,因此这个事件绑定将会造成内存泄露。在IBM的文章中介绍了2种方式解决类似的问题一个是obj=null,另一个是把onclick的函数写在bindEvent外。简单贴下代码:
function bindEvent() { var obj=document.createElement("XXX"); obj.onclick=onclickHandler; } function onclickHandler(){ //do something } |
function bindEvent() { var obj=document.createElement("XXX"); obj.onclick=function(){ //Even if it's a empty function } obj=null; } |
- 这两个方法都打断了循环引用,可以解决问题,但是似乎对代码表达能力造成了一定破坏,假设有这么一个问题:
function bindEvent( ) { var obj = document.createElement("XXX"); var var0 = "str_0”; //Here is a variable obj.onclick=function( ){ alert(var0); // I want to visit var here! } return obj; // bindEvent must return obj! } |
- 好了,这下两种办法都不行了,假如我把函数写外面去,var0肯定访问不了,假如我把obj弄成null,还怎么return它呢?这并不是空想的需要,这实际 上是一个用JS定制DOM控件的简单抽象:创建DOM元素、设置私有属性、绑定事件。所以,我们必须update一下两个方法。首先,方法1,为了让函数 能访问某些变量,我们可以通过一个Builder函数来订制onclick的外部闭包:
| function bindEvent() { var obj=document.createElement("XXX"); var var0="OOXX";//Here is a variable obj.onclick= onclickBuilder(var0);//想访问谁就把谁传进去!! return obj;//bindEvent must return obj! } function onclickBuilder(var0)//这里跟上面对应上就行了 最好参数名字也对应上 { return function(){ alert(var0); } } |
- 第二个办法,让obj=null在return 之后执行。
function bindEvent() { try{ var obj=document.createElement("XXX"); var var0="OOXX";//Here is a variable obj.onclick=function(){ alert(var0);//I want to visit var2 here! } return obj;//bindEvent must return obj! } finally { obj=null; } }
|
- 某些DOM操作
- 这是IE系列的特有问题 简单的来说就是在向不在DOM树上的DOM元素appendChild,可能会发生内存泄露(只是可能,具体原因不明,似乎十分复杂,下面例子中去掉onClick也可以避免泄露)。所以appendChild的顺序可能影响内存泄露:
- 自动类型装箱转换
原标题:浅谈 JS 内存泄露方式与避免方法(二)
关键词:JS
*特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们:
admin#shaoqun.com
(#换成@)。