Reputation: 6692
I have a memory leak I don't understand. I programmed a mechanism to handle event with semi-automatically unbinding which should allow me to clean up memory easily. But in one case, the clean up doesn't happen (I use chrome's "profile (memory heap)" to check for instances of "EventHandler" left). I really don't get why it happen. There's something weird with the closure...
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"); });
Upvotes: 6
Views: 1636
Reputation: 26828
You create closures that hold a reference to an EventHandler
instance via the handler
variable. One of the closures remains after the image has been loaded:
handler.addListener("ImageLoaded", handler, function() {
Loader.files[src] = function(fnct) {
fnct(img);
}
});
It's the inner function function(fnct) {...
. The instance of EventHandler
cannot be freed as long as the closure exists. Your only solution is to get rid of that closure. Or you free the instance manually, if possible. The following might work for you:
handler.fire("Delete");
handler = undefined;
Chrome's memory profiler shows you the object's retaining tree, which is just another way to say "Who is holding a reference that object". In your example it's EventHandler <- handler (the variable of the LoadImage method as incorporated by the closure) <- house.jpg, which actually is Loader.files[src]
and has the value function(fnct) { fnct(img); }
.
Upvotes: 2
Reputation: 5775
While you are adding 'listeners' make sure you are removing it, if you are using the query for long time.
this.listeners = new Object();
or
this.listeners[e] = new Object();
this will add object to listener as array, but not removing them at any point.
This may be the reason for Memory Consumption. It may not leak, its assignment of objects. that consumes your RAM using browser. :)
Upvotes: 2