Open GoogleCodeExporter opened 9 years ago
The following case gets the object to delete (manually kill the ref):
function makeClosure(forObj, f) {
var res = function () {
f(forObj);
};
res.wipe = function () {
forObj = null;
};
return res;
}
function tighty() {
var localObj = Obj();
var clos = makeClosure(localObj, function (obj) {
out("hi" + obj.x);
});
localObj.foobar = clos;
return localObj;
}
var theObj = tighty();
theObj.foobar();
theObj.foobar.wipe();
theObj;
Original comment by csaft...@gmail.com
on 17 Jul 2013 at 7:40
This work-around works:
var x = 9999;
function tighty() {
var localObj = Obj();
localObj.foobar = safeBind(localObj, function (localObj) {
out("hi" + localObj.x);
});
return localObj;
}
var theObj = tighty();
theObj.foobar();
theObj;
given the following classes:
class SafelyBound(object):
"""Apparently python gc likes this more than lambdas."""
def __init__(self, py_obj, js_func):
self.py_obj = py_obj
self.js_func = js_func
def __call__(self, *args):
self.js_func(self.py_obj, *args)
class PyObj(object):
def __init__(self):
self.x = 100
class Global(object):
def Obj(self):
return PyObj()
def out(self, v):
print "-- Script out: %s --" % (v,)
def safeBind(self, py_obj, js_func):
"""Bind in Python instead of JS to work around pyv8 memory leak."""
return SafelyBound(py_obj, js_func)
Interestingly, it *doesn't* work if safeBind is instead defined as a lambda:
def safeBind(self, py_obj, js_func):
"""Bind in Python instead of JS to work around pyv8 memory leak."""
return lambda *args: js_func(py_obj, *args)
Original comment by csaft...@gmail.com
on 17 Jul 2013 at 8:24
Garbage collection works properly if it's a javascript object that is
containing the python object:
var x = 9999;
function tighty() {
var jsObj = {};
jsObj.pyObj = Obj();
jsObj.foobar = function () {
out("hi" + jsObj.pyObj.x);
};
return jsObj;
}
var theObj = tighty();
theObj.foobar();
theObj;
Original comment by csaft...@gmail.com
on 17 Jul 2013 at 9:24
Update: GC also works if there are JS objects nested in JS Objects which end up
in a python object. Given:
class PyObj(PyV8.JSClass):
def __init__(self, name):
self.name = name
self.x = 100
def __del__(self):
print "PyObj(%s) __del__" % (self.name,)
class Global(object):
def Obj(self, name):
return PyObj(name)
def out(self, v):
print "-- Script out: %s --" % (v,)
Then for the following case:
run_case("deepnesting-ok", """
var x = 9999;
function tighty() {
var this_ = {};
this_.localObj = {};
this_.localObj.bark = {};
this_.localObj.bark.haha = {};
this_.localObj.bark.haha.bazoonga = Obj("bazoonga");
this_.foobar = function () {
out("hi" + this_.localObj.bark.haha.bazoonga.x);
};
return this_;
}
var theObj = tighty();
theObj.foobar();
theObj;
""")
'bazoonga' is collected:
Case deepnesting-ok:
-- Script out: hi100 --
refcount: 2
V8 theObj=null
Py obj=None
**Obj deleted**
V8 gc
PyObj(bazoonga) __del__
Py gc
Py gc.garbage: []
However, for the following case:
run_case("deepnesting-screwed", """
var x = 9999;
function tighty() {
var this_ = {};
this_.localObj = {};
this_.localObj.bark = Obj("bark");
this_.localObj.bark.haha = {};
this_.localObj.bark.haha.bazoonga = Obj("bazoonga");
this_.foobar = function () {
out("hi" + this_.localObj.bark.haha.bazoonga.x);
};
return this_;
}
var theObj = tighty();
theObj.foobar();
theObj;
""")
'bark' is collected, but 'bazoonga' is not:
Case deepnesting-screwed:
-- Script out: hi100 --
refcount: 2
V8 theObj=null
Py obj=None
**Obj deleted**
V8 gc
PyObj(bark) __del__
Py gc
Py gc.garbage: []
Original comment by csaft...@gmail.com
on 17 Jul 2013 at 10:10
Hmm I just realized at least some of the above might have to do with toString()
of JSClass being called & thus exhibiting issue 185.
Original comment by csaft...@gmail.com
on 17 Jul 2013 at 10:21
Seems it's ok to put py objects onto a js object, and py objects onto those py
objects, but you can't have a Py object on a JS object on a Py object - it
won't get GCed (attached whole file):
run_case("deepnesting-screwed", """ //FAIL
var x = 9999;
function tighty() {
var this_ = {};
var haha = Obj("Haha"); //collected
haha.zomg = Obj("zomg?"); //collected
this_.localObj = {};
this_.localObj.bark = Obj("bark"); //collected
this_.localObj.bark.bore = Obj("bore"); //collected
this_.localObj.bark.bore.fork = Obj("fork"); //collected
this_.localObj.bark.js = {};
this_.localObj.bark.js.bazoonga = Obj("bazoonga"); //** not collected **
return this_;
}
var theObj = tighty();
theObj;
""")
Original comment by csaft...@gmail.com
on 18 Jul 2013 at 3:36
Attachments:
To summarize: The bug seems to be whenever you have a chain of Python -> JS ->
Python objects. That is:
* JS -> Python: this is ok (e.g. python obj in a JS container, or as a local
variable of a JS function, or it is referenced in a JS closure).
* JS -> Python -> JS: this *might be* ok (e.g. python obj is in a container or
is a local var, and it contains JS objects). the python obj gets freed, so I
assume the JS one gets garbage collected, but I have no way of checking.
* JS -> Python -> JS -> Python: this is *not ok*. (e.g. python obj is in a
container os is a local var, and it contains a JS object that itself then
contains a Python object, or a python object contains a closure which
references another python object). In this case, the 2nd python object in the
chain doesn't get GCd.
Hopefully these memory leak issues can be resolved - otherwise PyV8 has been
quite great!
Original comment by csaft...@gmail.com
on 18 Jul 2013 at 3:55
Original issue reported on code.google.com by
csaft...@gmail.com
on 17 Jul 2013 at 7:29Attachments: