2013-04-07 139 views
6

我有内存泄漏,我不明白。我编写了一个机制来处理半自动解除绑定的事件,这可以让我轻松地清理内存。 但在一种情况下,清理不会发生(我使用chrome的“配置文件(内存堆)”来检查“EventHandler”左侧的实例)。我真的不明白为什么会发生。有一些奇怪的与封闭...Javascript关闭:内存泄漏

see it in action with chrome

function Bind(obj, f) { 
    return function() { 
     return f.apply(obj, arguments); 
    } 
} 

function EventHandler() { 
    this.listeners = new Object(); 

    var _listenerID = 0; 
    this.addListener = function(e, obj, listener, specialDisplay) { 
     if (typeof(listener) === "function") { 
      var listenerID = ++_listenerID; 
      console.log("Events (" + (++EventHandler.All) + ", " + listenerID + ") ++" + e); 

      if (!this.listeners.hasOwnProperty(e)) { 
       this.listeners[e] = new Object(); 
      } 
      this.listeners[e][listenerID] = listener; 

      if (obj != null && typeof(obj.removeListener) == "function") { 
       var deleteListenerID = obj.addListener("Delete", null, Bind(this, function() { 
        this.removeListener(e, listenerID); 
        obj.removeListener("Delete", deleteListenerID); 
       })); 
      } 

      return listenerID; 
     } 

     return null; 
    } 
    this.fire = function(e, obj) { 
     if (this.listeners.hasOwnProperty(e)) { 
      for(var i in this.listeners[e]) { 
       this.listeners[e][i](obj); 
      } 
     } 
    } 
    this.removeListener = function(e, listenerID) { 
     if (this.listeners.hasOwnProperty(e) && this.listeners[e].hasOwnProperty(listenerID)) { 
      delete this.listeners[e][listenerID]; 

      console.log("Events (" + (--EventHandler.All) + ", " + listenerID + ") --" + e); 
     } 
    } 
} 

EventHandler.All = 0; 

function Loader() { 
} 

Loader.files = new Object(); 

Loader.LoadImage = function(src, f) { 
    if (!Loader.files.hasOwnProperty(src)) { 
     var handler = new EventHandler(); 

     console.log("Loading.... (" + src + ")"); 

     Loader.files[src] = function(fnct) { 
      handler.addListener("ImageLoaded", handler, function(img) { 
       fnct(img); 
      }); 
     } 

     handler.addListener("ImageLoaded", handler, function() { 
      Loader.files[src] = function(fnct) { 
       fnct(img); 
      } 
     });  

     var img = new Image(); 
     $(img).load(function() { 
      console.log("Loaded.... (" + src + ")"); 
      handler.fire("ImageLoaded", img); 
      handler.fire("Delete"); 
      $(img).unbind('load'); 
     }); 
     img.src = src; 
    } 

    Loader.files[src](f); 
} 

Loader.LoadImage("http://serge.snakeman.be/Demo/house.jpg", function() { alert("ok"); }); 
+0

你可以请做,因为错误消息说,并粘贴代码从jsfiddle的问题。谢谢。 – JJJ 2013-04-07 15:24:03

+0

我真的不知道代码的哪一部分会更有意义,实际上我对stackoverflow警告有点不满。 – Serge 2013-04-07 15:31:19

+0

如果有人可以upvote我的问题,我会给予50赏金的答案... – Serge 2013-04-14 15:14:53

回答

2

创建持有经handler变量的引用到EventHandler实例关闭。其中一个封闭的保持图像加载后:

handler.addListener("ImageLoaded", handler, function() { 
     Loader.files[src] = function(fnct) { 
      fnct(img); 
     } 
    });  

它的内部函数function(fnct) {...。只要闭包存在,EventHandler的实例就不能被释放。你唯一的解决方案是摆脱封闭。或者如果可能的话,手动释放实例。下面会为你工作:

handler.fire("Delete"); 
handler = undefined; 

Chrome的内存分析器会显示该对象的保留树,这只是另一种说法:“谁持有的引用对象”。在你的例子是事件处理器 < - 处理程序(如并入由封闭所述的LoadImage方法的变量)< - house.jpg,这实际上是Loader.files[src]和具有值function(fnct) { fnct(img); }

+0

当图像被加载时,参考保持在Loader中。由于我将它设置为另一个值,文件[src]被删除。 Loader.files [src] = function(fnct)fnct(img); } – Serge 2013-04-22 10:23:23

+0

你是对的,当然,我错过了。但是这并没有太大的改变:这个函数阻止了EventHandler实例被释放(称为分析器)。我不是100%确定的,但只要函数存在,就可以引用处理程序变量,EventHandler实例将不会被释放。 – zeroflagL 2013-04-22 11:29:36

+0

对于不存在的函数,你建议做什么?无论如何,我无法得到它可以持有的地方。 :S – Serge 2013-04-22 11:33:28

2

在添加“侦听器”时,如果长时间使用查询,请确保删除它。

this.listeners = new Object(); 

this.listeners[e] = new Object(); 

这将到收听者添加对象作为数组,但没有在任何点移除它们。

这可能是内存消耗的原因。它可能不会泄漏,它的对象分配。使用浏览器消耗你的RAM。 :)

+0

我正在删除它们。在控制台上,您可以看到“Events(0,4)--Delete”,表示最后一个剩余的监听器已被删除。 – Serge 2013-04-23 07:08:01

+0

当您在系统中处理侦听器时的一般做法。 您需要处理它们,包括删除它们。在某些情况下,这同样适用于浏览器:)如果你删除它们,那么它就不在我的范围之内。但为了进行双重检查,请检查控制台'listeners []'中的元素。是否有元素。 – MarmiK 2013-04-23 07:30:45

+0

将对象添加到控制台,使chrome拥有另一个引用。但是因为你只是在谈论听众[]是不应该伤害尝试(我今天晚上会尝试)。 – Serge 2013-04-23 07:59:03