So the first point may be that objects may have references in many
contexts, luckily we prevent this from happening.
What I've tried:
I first create a new Context though JS_NewContext.
I then call JS_NewObject on that context with a JSClass I alloc'd, with
NULL proto and parent.
I then call JS_InitStandardClasses on that context with the object
created above.
I then call JS_SetGlobalObject with the object created above.
After which, all objects created for a context use the above object as
the parent to JS_NewObject.
The issue I'm finding is that even if I remove all roots, and call
JS_ClearScope on the above object before a call to GC, various objects
from that context won't get GC'd until after the context is destroyed.
Is there a way to terminate all references from a context? Or perhaps
I'd doing something wrong? I'd glading add relevant info to the JS API
wiki.
Thanks,
- Alex
> I then call JS_InitStandardClasses on that context with the object
> created above.
>
> I then call JS_SetGlobalObject with the object created above.
FYI, there's no need for this (it doesn't hurt), because
JS_InitStandardClasses sets the context's globalObject member to the
|obj| parameter you pass in, iff cx->globalObject is null.
> After which, all objects created for a context use the above object as
> the parent to JS_NewObject.
>
> The issue I'm finding is that even if I remove all roots, and call
> JS_ClearScope on the above object before a call to GC, various objects
> from that context won't get GC'd until after the context is destroyed.
> Is there a way to terminate all references from a context? Or perhaps
> I'd doing something wrong?
You should JS_ClearNewbornRoots(cx) as well.
You might use GC_MARK_DEBUG (define it before building the engine) to
dump the GC's heap before and after you destroy the context, but in both
cases after you think you've cleared everything that might entrain an
object associated by your embedding's customs with the context to be
destroyed. Sort and diff the heaps to see what you missed, that was not
missed by the GC done from JS_DestroyContext.
> I'd glading add relevant info to the JS API wiki.
Thanks -- a tutorial on using GC_MARK_DEBUG might be the best result.
/be
So thanks to that tool I see I was down from 2200 non strings to 67
with the added JS_ClearNewbornRoots call. What I see are all the
built-in objects dependant on the global object I created.
i.e. :
061f6810 object 06202ED8 Root via global object(Root @ 0x061f6810).
061f6858 object 061F6860 Object via global object(Root @
0x061f6810).__proto__(Object @ 0x061f6858).
061f6860 object 06324CE0 Function via global object(Root @
0x061f6810).__proto__(Object @ 0x061f6858).constructor(Function @
0x061f6860).
06327090 private 06327090 via global object(Root @
0x061f6810).__proto__(Object @ 0x061f6858).constructor(Function @
0x061f6860).slots().
06324ce0 private 06324CE0 via global object(Root @
0x061f6810).__proto__(Object @ 0x061f6858).constructor(Function @
0x061f6860).private().
061f6818 object 06324CC0 Function via global object(Root @
0x061f6810).__proto__(Object @ 0x061f6858).constructor(Function @
0x061f6860).__proto__(Function @ 0x061f6818).
it seems I'm missing one crucial step to get the "global object"
(cx->globalObject, Root above) to release itself. If I'm reading this
right, everything left seems to be dependant on itself.
any ideas?
Thanks so much,
- Alex
JS_GC->js_ForceGC ... so it seems the call to JS_ClearNewbornRoots is
unneeded. Perhaps js_ForceGC should actually call JS_ClearNewbornRoots
to make this clearer though.
I'll put a note about this in that method and create a guide about
GC_MARK_DEBUG.
Thanks again!
- Alex
No, the layering is such that other calls to js_ForceGC wouldn't benefit
from that move, or wouldn't want it. It's not totally clear to me how
this might matter, but API consumers may have come to depend on it in
the last 8 or 9 years (!), and I don't want to fiddle with it.
But note that only the cx on which you call JS_GC has its newborns
cleared, and it wasn't clear from what you wrote that that was the cx
that might be entraining stuff.
In other words, if you only called JS_DestroyContext(cx) and found some
object that you wanted to be "only for use on that cx" still alive (saw
your followup about clearing the global object, cool), then called
JS_GC(someothercx), you might also suffer from stuff being entrained by
the newborn roots.
That was my point, but it sounds like it's not an issue anyway. And if
you were calling JS_GC(cx) before JS_DestroyContext(cx), then as you
note, cx's newborns will be cleared in time. But you shouldn't have to
call JS_GC(cx) before JS_DestroyContext(cx) -- let the destroy do the
last GC.
/be
A runtime has two contexts initialized as I described in the first
post. The second context is created and initialized before the first
is destroyed. What I'm noticing is that the second context's global
object is getting rooted to the first context's global object through
the following:
061f7db8 object 06202F18 Root via global object(Root @
0x061f6bc8).AnyName(Function @ 0x061f77c0).prototype(AnyName @
0x061f77d0).constructor(Function @ 0x061f8508).__proto__(Function @
0x061f7dc0).__proto__(Object @ 0x061f7df0).__parent__(Root @
0x061f7db8).
here 0x061f7db8 is the globalObject to the second context and
0x061f6bc8 is the first context's global object.
This doesn't happen if I don't call JS_InitStandardClasses on the first
context. So it seems that when I call JS_InitStandardClasses on a
context in a runtime which has an existing context with standard
classes, the new context's root object somehow gets rooted to the other
context's root object. This doesn't sound like the desired behavior.
Any ideas?
Thanks,
- Alex
This sounds like a bug, and possibly a bad one even for Firefox's use,
if we entrain the first global beyond its natural lifespan.
js_GetAnyName appears, to my tired eyes, to be creating an object
associated with a context's global (and Object.prototype?), and then
keeping it around for what is possibly much longer than the context's
then-current global would otherwise live. We could sever the parent
link directly in js_GetAnyName, but I'm not sure at this hour if
that's the right thing, and it would leave the Object.prototype link
intact -- if that's a real problem indeed.
I'll look again in the AM, time willing, but a pessimistic bug filing
on the issue would not be unwelcome, IMO.
Mike
Root must be your global objects' class name.
> via global object(Root @
> 0x061f6bc8
The first context's global object, you wrote further on.
> ).AnyName(Function @ 0x061f77c0)
the AnyName constructor is named by the 'AnyName' property of this first
global object.
> .prototype(AnyName @
> 0x061f77d0)
AnyName.prototype
> .constructor(Function @ 0x061f8508)
which has a 'constructor' property which should be the AnyName
constructor traced from 'AnyName' in the first global object, but it's
not! "Don't cross the streams!"
.__proto__(Function @
> 0x061f7dc0).__proto__(Object @ 0x061f7df0).__parent__(Root @
> 0x061f7db8).
This other AnyName has a proto slot linking to a Function.prototype
whose proto links to Object.prototype whose parent is the second
context's global object.
> here 0x061f7db8 is the globalObject to the second context and
> 0x061f6bc8 is the first context's global object.
>
> This doesn't happen if I don't call JS_InitStandardClasses on the first
> context.
It looks to me as though you call JS_InitStandardClasses on the first
context after you called JS_InitStandardClasses on the second, *and*
from a function scoped by the second context's global (directly or not).
Exactly what code calls JS_InitStandardClasses in all cases in your
embedding?
/be
Look closer -- the problem you cite is not (see my followup) Alex's
problem. His embedding appears to be scoping one global object's
JS_InitStandardClasses using another context's global object.
> js_GetAnyName appears, to my tired eyes, to be creating an object
> associated with a context's global (and Object.prototype?),
Associated with is too strong -- parented by (and prototyped by).
> and then
> keeping it around for what is possibly much longer than the context's
> then-current global would otherwise live.
No longer than anyname is needed. The JSRuntime.anynameObject reference
is a weak one, so as soon as the GC can, it will collect this object and
the finalizer will clear rt->anynameObject. The cycle can repeat on
demand and anynameObject can be shared across contexts that use * in E4X
code. Note that * is an intern'ed object, not accessible to JS as an
object reference.
You're right that scripts compiled using the first context's global may
keep that context's global alive as long as the scripts live. That is
not a problem in content windows in general, any more than literals of
other object type keeping the window in which the literal was compiled
alive.
With chrome windows loading long-lived scripts, there could be a worse
entrainment problem. I'll take that bug, but cheer up -- no need to be
pessimistic when filing it!
/be
I think I just realized what the problem is...for the second context,
in JS_InitClass for the AnyName class, it's setting the
cx2->globalObject.AnyName function property to the rt->anynameObject
(created from cx1). Then, because it thinks it just constructed a new
AnyName object it set's cx1->globalObject.AnyName.constructor to
cx2->globalObject.AnyName...and it set's
cx2->globalObject.AnyName.prototype to cx1->globalObject.AnyName.
I think this means cx1->globalObject.AnyName can't be GC'd until
cx2->globalObject.AnyName...which in turn can't get put up for GC until
cx1->globalObject.AnyName is put for GC....circular dependency my
friends :)
could rt->anynameObject be moved to cx->anynameObject ? seems like
that would solve the problem.
Thanks,
- Alex
should I submit a patch and put a note in the JSRuntime struct on why
not to add objects to it?
- Alex
That can't be, the AnyName property's value is a function object, not an
anyname object. Do you mean AnyName.prototype? That'd be a problem!
> Then, because it thinks it just constructed a new
> AnyName object it set's cx1->globalObject.AnyName.constructor to
cx1->globalObject.AnyName.prototype.constructor, right.
> cx2->globalObject.AnyName...and it set's
> cx2->globalObject.AnyName.prototype to cx1->globalObject.AnyName.
>
> I think this means cx1->globalObject.AnyName can't be GC'd until
> cx2->globalObject.AnyName...which in turn can't get put up for GC until
> cx1->globalObject.AnyName is put for GC....circular dependency my
> friends :)
Yeah, it's ugly.
> could rt->anynameObject be moved to cx->anynameObject ? seems like
> that would solve the problem.
The * identifier has to be runtime-wide, or it won't match in E4X
expressions evaluated "across contexts" -- the match uses pointer identity.
Please do file a bug on this.
/be
Patch in bug, singletons remain, comment added to jscntxt.h.
/be