2013-04-07 13 views
6

Tôi bị rò rỉ bộ nhớ mà tôi không hiểu. Tôi đã lập trình một cơ chế để xử lý sự kiện với tính năng tự động bán tự động, cho phép tôi dọn dẹp bộ nhớ dễ dàng. Nhưng trong một trường hợp, việc dọn dẹp không xảy ra (tôi sử dụng "hồ sơ (bộ nhớ heap)" của chrome để kiểm tra các phiên bản "EventHandler" bên trái). Tôi thực sự không hiểu tại sao nó lại xảy ra. Có điều gì đó kỳ lạ với việc đóng cửa ...Đóng cửa Javascript: Memory Leak

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

Bạn có thể làm như thông báo lỗi nói và dán mã từ jsfiddle vào câu hỏi. Cảm ơn. – JJJ

+0

Tôi thực sự không biết những gì một phần của mã sẽ là có ý nghĩa hơn tôi đã thực sự một chút bối rối bởi cảnh báo stackoverflow. – Serge

+0

Nếu ai đó có thể upvote câu hỏi của tôi, tôi sẽ cấp 50 tiền thưởng cho câu trả lời ... – Serge

Trả lời

2

Bạn tạo đóng cửa giữ một tham chiếu đến một thể hiện EventHandler qua biến handler. Một trong những đóng cửa vẫn còn sau khi hình ảnh đã được tải:

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

Đó là hàm bên trong function(fnct) {.... Không thể giải phóng trường hợp của EventHandler miễn là đóng cửa tồn tại. Giải pháp duy nhất của bạn là loại bỏ việc đóng cửa đó. Hoặc bạn giải phóng cá thể thủ công, nếu có thể. Sau đây có thể làm việc cho bạn:

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

profiler bộ nhớ của Chrome hiển thị bạn cây giữ của đối tượng, mà chỉ là một cách khác để nói "Ai đang nắm giữ một tham chiếu đối tượng". Trong ví dụ của bạn đó là EventHandler < - handler (biến của phương pháp LoadImage như kết hợp bằng việc đóng cửa) < - house.jpg, mà thực sự là Loader.files[src] và có giá trị function(fnct) { fnct(img); }.

+0

Khi hình ảnh được tải giữ tham chiếu trong Trình tải.các tệp [src] bị xóa vì tôi đặt nó thành một giá trị khác. Loader.files [src] = function (fnct) { fnct (img); } – Serge

+0

Bạn đúng, tất nhiên, tôi đã bỏ lỡ điều đó. Nhưng điều đó không thay đổi nhiều: chức năng này ngăn không cho cá thể EventHandler được phát hành (trình bày hồ sơ). Tôi không chắc chắn 100%, nhưng miễn là một hàm tồn tại, có thể tham chiếu biến xử lý, cá thể EventHandler sẽ không được giải phóng. – zeroflagL

+0

Bạn đề xuất gì cho chức năng không tồn tại nữa? Tôi không nhận được nơi nó có thể được giữ anyway. : S – Serge

2

Trong khi bạn thêm 'người nghe' đảm bảo bạn đang xóa nó, nếu bạn đang sử dụng truy vấn trong thời gian dài.

this.listeners = new Object(); 

hoặc

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

này sẽ bổ sung thêm đối tượng để người nghe như mảng, nhưng không loại bỏ chúng bất cứ lúc nào.

Đây có thể là lý do cho Mức tiêu thụ bộ nhớ. Nó có thể không bị rò rỉ, sự phân công của nó đối tượng. tiêu thụ RAM của bạn bằng trình duyệt. :)

+0

Tôi đang xóa chúng. Trên giao diện điều khiển, bạn có thể thấy "Sự kiện (0, 4) - Xóa" cho biết người nghe cuối cùng còn lại đã bị xóa. – Serge

+0

Thực hành chung của nó khi bạn đang giao dịch với người nghe trong hệ thống. Bạn cần xử lý chúng bao gồm xóa chúng. Điều tương tự cũng áp dụng cho trình duyệt trong một số trường hợp :) Nếu bạn đang loại bỏ chúng thì nó không nằm ngoài tầm với của tôi. nhưng để kiểm tra lại, hãy kiểm tra phần tử trong bảng điều khiển 'người nghe []'. Cho dù nó có yếu tố hay không. – MarmiK

+0

Thêm đối tượng vào bảng điều khiển, làm cho chrome giữ một tham chiếu khác trên chúng. Nhưng kể từ khi bạn chỉ nói về những người nghe [] là không nên làm tổn thương cố gắng (Tôi sẽ cố gắng tối nay). – Serge