js的闭包和回调到底怎么才会造成真的内存泄漏呢?


最近看了一些分享的js内存泄漏典型案例,感觉这种代码以前一直写,现在不留意的话也一直这么写,到现在发现写代码的时候老怕闭包和回调了,在前端出现泄露的话问题还不算非常严重,但如果后端使用nodejs写出现内存泄露的话,这可会让服务崩溃。
为了严谨性,到这里问下各位技术达人。


 //一下是angularjs的controller模拟
function($scope){
    $scope.todo1 = todo1;
    $scope.todo2 = todo2;
    $scope.todoAll = {
          todo1 : todo1..
      }
}

像这样的回调其实就是一种闭包,那么这个时候会造成内存泄露吗?
再看这里


 
function foo($scope){ var text = 'you click me!!!'; var clickHanlder = function(){ alert(text); } $scope.clickHanlder = clickHanlder; } var $a = {} foo($a); $a.clickHanlder(); //这里的text被提到全局的作用域了,这种算内存泄露吗?如果$a是DOM呢?

 var gloablObject = {}
var addCallback = function(hanlder){
    //这样的闭包回调有问题吗?
    var value = function(){
        var temp;
        if(true){
            temp = new Value(true);
        }else{
            temp = new Value();
        }
        return temp;
    }

    hanlder(value);
}
addCallback(function(value){
    //1 这里有问题吗?
    globalObject.ref = value;

    //2 这里呢?
    globalObject.ref = function(nv){
        alert(nv.todo(value));
    }

});

还有


 //下面的代码大致模拟一下angularjs的写法
fn(function($add){
    var pro1 = 'pro1';
    var pro2 = 'pro2';
    return function(){
        $add(pro1,pro2);
    }
});

类似这些写法很经常,现在遇到闭包和回调我都分不出我写的代码会不会造成内存泄露了,js坑很大啊!!

node.js 回调函数 JavaScript 闭包 内存泄漏

音姐最萌不解释 11 years, 10 months ago

兄弟,不必担心。
首先,能导致内存泄漏的一定是引用类型的变量 ,比如函数和其他自定义对象。而 值类型 的变量是不存在内存泄漏的,比如字符串、数字、布尔值等。
因为值类型是靠复制来传递的,而引用类型是靠类似c语言中的指针来传递的。
可以认为一个引用类型的变量就是一个指向某个具体的内存地址的指针

当我们用js代码创建一个引用类型的时候(以下简称对象),js引擎会在内存中开辟一块空间来存放数据,并把指针引用交给那个变量。内存是有限的,js引擎必须保证当开辟的对象没用的时候,把所分配的内存空间释放出来,这个过程叫做 垃圾回收 ,负责回收的叫做垃圾回收器(GC)。

OK, 内存泄漏是指我们已经无法再通过js代码来引用到某个对象,但垃圾回收器却认为这个对象还在被引用,因此在回收的时候不会释放它 。导致了分配的这块内存永远也无法被释放出来。如果这样的情况越来越多,会导致内存不够用而系统崩溃。

不可控的东西才是最可怕的!

最经典的例子就是外部我们不可控的引用。比如说IE6中dom对象引用了js对象,而dom对象在某个时刻被移除掉了,但js引擎不知道它被移除掉,还傻傻的保留着引用呢,就不会把js对象释放。(ie7+改善很多了,我不是黑IE)

然后就是闭包中的引用了。咱们使用闭包的目的,就是要保存内部变量的状态以便我们哪个时候去通过闭包使用它作用域内的变量。

我们可以把闭包形象的理解为一道门,屋子里面是内部变量。钥匙是一个引用。
当我们把钥匙给张三这个对象(otherObject1.p1 -> 门),产生了一个引用
当我们再配一把钥匙给李四这个对象(otherObject2.p2 -> 门)产生了另外一个引用

GC在回收的时候会判断一个闭包还有没有人拿着钥匙,要是没有引用或者是内部循环引用(李四在屋子里),就会释放闭包内变量所在的空间,回收垃圾

我斗胆的说一句:严格意义上讲,闭包不是真正产生内存泄漏的原因! 各位有意见可以评论里指出,现在举个最简单的例子:


 function bindEvent() 
{ 
    var obj = document.createElement("XXX"); 
    obj.onclick = function(){ 
        // ... 
    } 
}

bindEvent();

这人把钥匙(引用)给了一个外部不可控的dom对象,怎么能怪人家闭包的错误呢!
再看下面代码:


 var otherJsObj = {};
function bind() 
{ 
    otherJsObj.func1 = function(){ 
        // ...
    } 
}

bind();

我把钥匙给了otherJsObj。然后叮嘱它:“你不用的时候就把你的func1置空或者赋值成别的对象,解除我的引用,我好回收垃圾”。这样可控,因为咱们都是自己人(js对象),有访问权限[呵呵]

楼主会产生这样的困惑,是因为闭包确实是在保持对别的对象的引用。也会产生较大的内存占用。但这是可控制的,不是闭包的错。
谢谢!

蓝精灵主教 answered 11 years, 10 months ago

Your Answer