Discussion:
bind() / memory leak?
Kevin Kaiser
2009-06-19 15:34:14 UTC
Permalink
I've built a rather complicated web app that makes heavy use of
MochiKit.Base.bind() and partial(). There are no page refreshes in the
app and the majority of the page content between areas of the UI is
dynamically created / removed via DOM methods. A user might spend
considerable time inside the app and it leaks memory like I've never
seen.

I suspect it has a lot to do with the fact that the majority of the
data in browser memory is inside of a single, potentially-large
object / data structure, and most bind() or partial() calls pass along
this object as a parameter, which ends up set as the im_self attribute
on the resulting bound function.

A lot of my bound functions end up set as event handlers and things
like that, so when the app tears down part of the screen to display
new stuff, those functions just get popped out of the DOM but aren't
garbage collected by the browser since the page never reloads.

Is there any way to force garbage collection while a page is still
loaded in any of the browsers?

I may need to build some kind of explicit cleanup functions that
attach to an element and clean these references upon the element's
removal from the DOM, except I fear it being really slow due to the
fact that if you remove a parent element that has a huge amount of
[great-great][grand]child nodes, I'll have to walk the whole tree and
clean each individual node..

Anyhow, any ideas would be wonderful.

Thanks!
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "MochiKit" group.
To post to this group, send email to ***@googlegroups.com
To unsubscribe from this group, send email to mochikit+***@googlegroups.com
For more options, visit this group at http://groups.google.com/group/mochikit?hl=en
-~----------~----~----~----~------~----~------~--~---
Jeryl Cook
2009-06-19 16:10:09 UTC
Permalink
Have you tried jProbe? it will help you figure out where the memory
leak is..it could be anywhere not necessarily MochiKit.

http://www.quest.com/jprobe/
its commercial but there is a trial.
Post by Kevin Kaiser
I've built a rather complicated web app that makes heavy use of
MochiKit.Base.bind() and partial(). There are no page refreshes in the
app and the majority of the page content between areas of the UI is
dynamically created / removed via DOM methods. A user might spend
considerable time inside the app and it leaks memory like I've never
seen.
I suspect it has a lot to do with the fact that the majority of the
data in browser memory is inside of a single, potentially-large
object / data structure, and most bind() or partial() calls pass along
this object as a parameter, which ends up set as the im_self attribute
on the resulting bound function.
A lot of my bound functions end up set as event handlers and things
like that, so when the app tears down part of the screen to display
new stuff, those functions just get popped out of the DOM but aren't
garbage collected by the browser since the page never reloads.
Is there any way to force garbage collection while a page is still
loaded in any of the browsers?
I may need to build some kind of explicit cleanup functions that
attach to an element and clean these references upon the element's
removal from the DOM, except I fear it being really slow due to the
fact that if you remove a parent element that has a huge amount of
[great-great][grand]child nodes, I'll have to walk the whole tree and
clean each individual node..
Anyhow, any ideas would be wonderful.
Thanks!
--
Jeryl Cook
/^\ Pharaoh /^\
http://pharaohofkush.blogspot.com/
I have long since come to believe that people never mean half of what
they say, and that it is best to disregard their talk and judge only
their actions.
-Dorothy Day

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "MochiKit" group.
To post to this group, send email to ***@googlegroups.com
To unsubscribe from this group, send email to mochikit+***@googlegroups.com
For more options, visit this group at http://groups.google.com/group/mochikit?hl=en
-~----------~----~----~----~------~----~------~--~---
David Barnett
2009-06-20 00:24:50 UTC
Permalink
Isn't jProbe specifically for profiling Java code? I don't understand how
that would help. Or is Java just the implementation language?

David
Post by Jeryl Cook
Have you tried jProbe? it will help you figure out where the memory
leak is..it could be anywhere not necessarily MochiKit.
http://www.quest.com/jprobe/
its commercial but there is a trial.
Post by Kevin Kaiser
I've built a rather complicated web app that makes heavy use of
MochiKit.Base.bind() and partial(). There are no page refreshes in the
app and the majority of the page content between areas of the UI is
dynamically created / removed via DOM methods. A user might spend
considerable time inside the app and it leaks memory like I've never
seen.
I suspect it has a lot to do with the fact that the majority of the
data in browser memory is inside of a single, potentially-large
object / data structure, and most bind() or partial() calls pass along
this object as a parameter, which ends up set as the im_self attribute
on the resulting bound function.
A lot of my bound functions end up set as event handlers and things
like that, so when the app tears down part of the screen to display
new stuff, those functions just get popped out of the DOM but aren't
garbage collected by the browser since the page never reloads.
Is there any way to force garbage collection while a page is still
loaded in any of the browsers?
I may need to build some kind of explicit cleanup functions that
attach to an element and clean these references upon the element's
removal from the DOM, except I fear it being really slow due to the
fact that if you remove a parent element that has a huge amount of
[great-great][grand]child nodes, I'll have to walk the whole tree and
clean each individual node..
Anyhow, any ideas would be wonderful.
Thanks!
--
Jeryl Cook
/^\ Pharaoh /^\
http://pharaohofkush.blogspot.com/
I have long since come to believe that people never mean half of what
they say, and that it is best to disregard their talk and judge only
their actions.
-Dorothy Day
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "MochiKit" group.
To post to this group, send email to ***@googlegroups.com
To unsubscribe from this group, send email to mochikit+***@googlegroups.com
For more options, visit this group at http://groups.google.com/group/mochikit?hl=en
-~----------~----~----~----~------~----~------~--~---
Per Cederberg
2009-06-20 06:39:14 UTC
Permalink
Mostly, these issues are caused by lingering references to the removed
DOM nodes (lookup tables, etc). In this case, since you use MochiKit,
you might want to look at the MochKit.Signal.disconnectAll().
It might also be helpful for some older browsers to tear down the DOM tree.

Here's a helper function that I've used to achieve these two aims:

/**
* Destroys a widget or a DOM node. This function will remove the DOM
* node from the tree, disconnect all signals and call all widget
* destructor functions. The same procedure will also be applied
* recursively to all child nodes. Once destroyed, all references to
* the widget object should be cleared in order for the browser to
* be able to reclaim the memory used.
*
* @param {Widget/Node/Array} node the (widget) DOM node or list
*
* @static
*/
MochiKit.Widget.destroyWidget = function (node) {
    if (node.nodeType != null) {
        if (typeof(node.destroy) == "function") {
            node.destroy();
        }
        if (node.parentNode != null) {
            MochiKit.DOM.removeElement(node);
        }
        MochiKit.Signal.disconnectAll(node);
        while (node.firstChild != null) {
            MochiKit.Widget.destroyWidget(node.firstChild);
        }
    } else if (MochiKit.Base.isArrayLike(node)) {
        for (var i = node.length - 1; i >= 0; i--) {
            MochiKit.Widget.destroyWidget(node[i]);
        }
    }
}

Cheers,

/Per
Post by Kevin Kaiser
I've built a rather complicated web app that makes heavy use of
MochiKit.Base.bind() and partial(). There are no page refreshes in the
app and the majority of the page content between areas of the UI is
dynamically created / removed via DOM methods. A user might spend
considerable time inside the app and it leaks memory like I've never
seen.
I suspect it has a lot to do with the fact that the majority of the
data in browser memory is inside of a single, potentially-large
object / data structure, and most bind() or partial() calls pass along
this object as a parameter, which ends up set as the im_self attribute
on the resulting bound function.
A lot of my bound functions end up set as event handlers and things
like that, so when the app tears down part of the screen to display
new stuff, those functions just get popped out of the DOM but aren't
garbage collected by the browser since the page never reloads.
Is there any way to force garbage collection while a page is still
loaded in any of the browsers?
I may need to build some kind of explicit cleanup functions that
attach to an element and clean these references upon the element's
removal from the DOM, except I fear it being really slow due to the
fact that if you remove a parent element that has a huge amount of
[great-great][grand]child nodes, I'll have to walk the whole tree and
clean each individual node..
Anyhow, any ideas would be wonderful.
Thanks!
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "MochiKit" group.
To post to this group, send email to ***@googlegroups.com
To unsubscribe from this group, send email to mochikit+***@googlegroups.com
For more options, visit this group at http://groups.google.com/group/mochikit?hl=en
-~----------~----~----~----~------~----~------~--~---
Kevin Kaiser
2009-06-29 13:33:32 UTC
Permalink
Thanks for the insight. I solved part of the problem by changing how
MochiKit.Base.bind stores the bound object (im_self); instead of
returning a function with the bound object as a direct property of the
function, it stores a numeric index to a table of objects instead.
When binding, if your target object has already been bound before, bind
() just reuses the existing index. It's significantly reduced the
amount of memory being used, although long-term usage of the app still
builds a large table of objects which needs pruned from time to time.
Our app has a state manager built in so I use it to prune the bound
objects list of things that got set up in a given state, upon exiting
that state.

Anyways, here's the updated MochiKit.Base.bind() if anybody is
interested:

window.onunload = function () {

delete MochiKit.Base.bindings;
delete MochiKit.Base.bound_objects;

};

MochiKit.Base.bindings = [];
MochiKit.Base.bound_objects = [];

/** @id MochiKit.Base.bind */
MochiKit.Base.bind = function (func, self/* args... */) {
if (typeof(func) == "string") {
func = self[func];
}
var im_func = func.im_func;
var im_preargs = func.im_preargs;
var im_self = func.im_self;
var m = MochiKit.Base;
if (typeof(func) == "function" && typeof(func.apply) ==
"undefined") {
// this is for cases where JavaScript sucks ass and
gives you a
// really dumb built-in function like alert() that
doesn't have
// an apply
func = m._wrapDumbFunction(func);
}
if (typeof(im_func) != 'function') {
im_func = func;
}
if (typeof(self) != 'undefined') {
im_self = self;
}

im_self_index = null;

for (var i = 0; i < MochiKit.Base.bound_objects.length; i
++) {

if (MochiKit.Base.bound_objects[i] === im_self) {

im_self_index = i;

} // end if

} // end for

if (im_self_index == null) {

MochiKit.Base.bound_objects.push(im_self);

im_self_index = MochiKit.Base.bound_objects.length -
1;

} // end if

im_self = MochiKit.Base.bound_objects[im_self_index];

if (typeof(im_preargs) == 'undefined') {
im_preargs = [];
} else {
im_preargs = im_preargs.slice();
}
m.extend(im_preargs, arguments, 2);
var newfunc = function () {
var args = arguments;
var me = arguments.callee;
if (MochiKit.Base.bindings
[me.index].im_preargs.length > 0) {
args = m.concat(MochiKit.Base.bindings
[me.index].im_preargs, args);
}
var self = MochiKit.Base.bindings[me.index].im_self;
if (!self) {
self = this;
}
return MochiKit.Base.bindings[me.index].im_func.apply
(self, args);
};

newfunc.index = MochiKit.Base.bindings.length;

MochiKit.Base.bindings.push({"im_self" : im_self,
"im_func" : im_func,
"im_preargs" :
im_preargs});

return newfunc;
};
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "MochiKit" group.
To post to this group, send email to ***@googlegroups.com
To unsubscribe from this group, send email to mochikit+***@googlegroups.com
For more options, visit this group at http://groups.google.com/group/mochikit?hl=en
-~----------~----~----~----~------~----~------~--~---

Loading...