簡體   English   中英

Javascript關閉:內存泄漏

[英]Javascript closure : Memory Leak

我有一個內存泄漏,我不明白。 我編寫了一個機制來處理事件,半自動解除綁定,這可以讓我輕松地清理內存。 但在一種情況下,清理不會發生(我使用chrome的“profile(內存堆)”來檢查“EventHandler”左側的實例)。 我真的不明白為什么會這樣。 關閉時有些奇怪......

用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"); });

您可以通過handler變量創建包含對EventHandler實例的引用的閉包。 加載圖像后,其中一個閉包仍然存在:

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

它是內部函數function(fnct) {... 只要閉包存在,就無法釋放EventHandler的實例。 你唯一的解決方案是擺脫那個關閉。 或者,如果可能,您手動釋放實例。 以下可能適合您:

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

Chrome的內存分析器會顯示對象的保留樹,這只是說“誰持有對象的引用”的另一種方式。 在你的例子中,它是EventHandler < - handler (由閉包合並的LoadImage方法的變量)< - house.jpg ,它實際上是Loader.files[src]並且具有值function(fnct) { fnct(img); } function(fnct) { fnct(img); }

在添加“偵聽器”時,如果長時間使用查詢,請確保將其刪除。

this.listeners = new Object();

要么

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

這會將對象作為數組添加到偵聽器,但不會在任何時候刪除它們。

這可能是內存消耗的原因。 它可能不泄漏,它的對象分配。 使用瀏覽器消耗你的RAM。 :)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM