6

我有一个我不明白的内存泄漏。我编写了一种机制来处理具有半自动解除绑定的事件,这应该可以让我轻松清理内存。但在一种情况下,清理不会发生(我使用 chrome 的“配置文件(内存堆)”来检查剩下的“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"); });
4

2 回答 2

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 <- handler(由闭包合并的 LoadImage 方法的变量) <- house.jpg,它实际上是Loader.files[src]并且具有 value function(fnct) { fnct(img); }

于 2013-04-21T16:27:31.087 回答
2

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

this.listeners = new Object();

或者

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

这会将对象作为数组添加到侦听器,但不会在任何时候删除它们。

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

于 2013-04-23T06:58:58.350 回答