2013-04-07 7 views
6

У меня утечка памяти, я не понимаю. Я запрограммировал механизм обработки события с полуавтоматическим развязыванием, которое должно позволить мне легко очищать память. Но в одном случае очистка не происходит (я использую хром-профиль (кучу памяти) ", чтобы проверить наличие экземпляров« 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

+0

Я действительно не знаю, какая часть кода была бы более значимой, я был на самом деле немного смущен предупреждением stackoverflow. – Serge

+0

Если кто-то может поднять мой вопрос, я дам 50 баунтин за ответ ... – Serge

ответ

2

создаются затворы, которые держат ссылку на EventHandler экземпляра через переменную handler. Одна из крышек остается после того, как было загружено изображение:

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

Это внутренняя функция function(fnct) {.... Экземпляр EventHandler не может быть освобожден до тех пор, пока существует закрытие. Ваше единственное решение - избавиться от этого закрытия. Или, если это возможно, вы производите экземпляр вручную. Далее может работать для вас:

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

профайлер памяти Chrome показывает вам удерживающий дерево объекта, который является просто еще один способ сказать: «Кто держит ссылку, что объект». В вашем примере это EventHandler < - обработчик (переменная метода LoadImage, входящее закрытие) < - house.jpg, который на самом деле является Loader.files[src] и имеет значение function(fnct) { fnct(img); }.

+0

Когда изображение загружается, ссылка сохраняется в Loader.файлы [src] отбрасываются, так как я устанавливаю его в другое значение. Loader.files [src] = function (fnct) { fnct (img); } – Serge

+0

Вы правы, конечно, я пропустил это. Но это не сильно меняет: эта функция предотвращает выпуск экземпляра EventHandler (говорит профилировщик). Я не уверен на 100%, но afaik до тех пор, пока существует функция, которая может ссылаться на переменную обработчика, экземпляр EventHandler не будет выпущен. – zeroflagL

+0

Что вы предлагаете сделать, чтобы функция не существовала больше? В любом случае, я не понимаю, где это может быть. : S – Serge

2

Пока вы добавляете «слушателей», убедитесь, что вы удаляете его, если вы используете запрос в течение длительного времени.

this.listeners = new Object(); 

или

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

это добавит объект слушателю как массив, но не удаляя их в любой момент.

Это может быть причиной потребления памяти. Он не может течь, его назначение объектов. который потребляет ваше ОЗУ с помощью браузера. :)

+0

Я их удаляю. На консоли вы можете увидеть «События (0, 4) - Удалить», которые указывают, что последний оставшийся прослушиватель удален. – Serge

+0

Его общая практика, когда вы имеете дело с слушателями в системе. Вам необходимо обработать их, включая их удаление. То же самое относится и к браузеру в некоторых случаях :) Если вы их удаляете, то это недоступно. но для двойной проверки проверьте элемент в консоли 'listeners []'. Есть ли у него элементы или нет. – MarmiK

+0

Добавление объекта в консоль, делает хром еще одной ссылкой на них. Но поскольку вы просто говорите о слушателях [], это не должно причинять вреда (я попробую сегодня вечером). – Serge