How to keep track of instance deletion in V8?

534 views
Skip to first unread message

deadmorous

unread,
Feb 21, 2010, 11:24:35 AM2/21/10
to v8-users
Hello,
I'm trying to use V8 to expose functionality of my app to JS.
In my app, there are objects with properties and methods, organized as
a tree of properties;
object instances can be created and added to the tree.
I understand how to expose my properties using V8 interceptors, and
think that it's correct to expose my
methods as properties of prototypes. Further, I would like objects to
be creatable from JS code,
so I would expose my native constructors to JS. It happens that I have
to have a duality between my instances
and JS instances. In my app, I would put a handle to JS instance
corresponding to my instance;
and vice versa, I can put a pointer to my instance into corresponding
JS instance, by using
Object::SetInternalField(), and using a value of type External.

Now to my question. What I really don't understand - how can I know
that a JS object is being destroyed?
I really need to know that, at least in order to destroy my instance
attached to the one being destroyed in JS.

Alfred Rossi

unread,
Feb 21, 2010, 11:31:21 AM2/21/10
to v8-u...@googlegroups.com
You're looking for Persistent::MakeWeak. You can use MakeWeak to specify
a callback to be invoked when the object is about to be garbage
collected.

You should be careful about building in an alternate way to free your C
++ objects. The garbage collector is guaranteed to be run, ever, even
after the context has been disposed.

See this for more details:
http://stackoverflow.com/questions/173366/how-do-you-free-a-wrapped-c-object-when-associated-javascript-object-is-garbage

Best,
Alfred

Alfred Rossi

unread,
Feb 21, 2010, 11:32:32 AM2/21/10
to v8-u...@googlegroups.com
I apologize, that was supposed to say "The garbage collector is NOT
guaranteed..."

Best,
Alfred

deadmorous

unread,
Feb 21, 2010, 12:17:51 PM2/21/10
to v8-users
Thank you very much, Alfred.
This seems to be exactly what I asked for. :) One thing I still don't
understand is the description of "object" parameter in
WeakReferenceCallback, saying "the weak global object to be reclaimed
by the garbage collector". Why "global"? What if the object was a
property of some other object, and the property was then removed?

Best regards,
Deadmorous

On 21 фев, 19:32, Alfred Rossi <alfr...@gmail.com> wrote:
> I apologize, that was supposed to say "The garbage collector is NOT
> guaranteed..."
>
> Best,
> Alfred
>
> On Sun, 2010-02-21 at 11:31 -0500, Alfred Rossi wrote:
> > You're looking for Persistent::MakeWeak. You can use MakeWeak to specify
> > a callback to be invoked when the object is about to be garbage
> > collected.
>
> > You should be careful about building in an alternate way to free your C
> > ++ objects. The garbage collector is guaranteed to be run, ever, even
> > after the context has been disposed.
>
> > See this for more details:

> >http://stackoverflow.com/questions/173366/how-do-you-free-a-wrapped-c...

Alfred Rossi

unread,
Feb 21, 2010, 12:44:25 PM2/21/10
to v8-u...@googlegroups.com
I think global here is referring to the global HandleScope and not the
owning object. I suspect that by holding it with a persistent handle
you globalize (free it's ties to C++ scope dependent handles like
Local) the object. According to the Embedder's Guide:

"Persistent handles are not held on a stack and are deleted only when
you specifically remove them. Just like a local handle, a persistent
handle provides a reference to a heap-allocated object. Use a
persistent handle when you need to keep a reference to an object for
more than one function call, or when handle lifetimes do not
correspond to C++ scopes...."

"...A persistent handle can be made weak, using Persistent::MakeWeak,
to trigger a callback from the garbage collector when the only
references to an object are from weak persistent handles."

The comments and documentation leave me with the impression that when
one object holds another it does not do so using a weak persistent
handle, and thus the garbage collector is prevented from invoking the
callback.

This gets into internals which I am not too keen on (I am just a user
like yourself), but there are developers on this list that can jump in
should you like to know more.

Best,
Alfred

> --
> v8-users mailing list
> v8-u...@googlegroups.com
> http://groups.google.com/group/v8-users

deadmorous

unread,
Feb 21, 2010, 3:28:45 PM2/21/10
to v8-users
Well, the most important to me is to take some action in responce to
adding/removing properties, which is done using interceptors. Once a
JS property is removed, I can reflect that in my own property tree,
and also call Persistent::MakeWeak() on the object referred to by the
property that's being removed. Then at some time GC would remove JS
instance, which in turn would dereference my instance, which in turn
would typically remove it. At least this is how I see that now - will
try to implement. V8's API is not obvious, though SpiderMonkey's is
harder to understand and use)

On 21 фев, 20:44, Alfred Rossi <alfr...@gmail.com> wrote:
> I think global here is referring to the global HandleScope and not the
> owning object. I suspect that by holding it with a persistent handle
> you globalize (free it's ties to C++ scope dependent handles like
> Local) the object. According to the Embedder's Guide:
>
> "Persistent handles are not held on a stack and are deleted only when
> you specifically remove them. Just like a local handle, a persistent
> handle provides a reference to a heap-allocated object. Use a
> persistent handle when you need to keep a reference to an object for
> more than one function call, or when handle lifetimes do not
> correspond to C++ scopes...."
>
> "...A persistent handle can be made weak, using Persistent::MakeWeak,
> to trigger a callback from the garbage collector when the only
> references to an object are from weak persistent handles."
>
> The comments and documentation leave me with the impression that when
> one object holds another it does not do so using a weak persistent
> handle, and thus the garbage collector is prevented from invoking the
> callback.
>
> This gets into internals which I am not too keen on (I am just a user
> like yourself), but there are developers on this list that can jump in
> should you like to know more.
>
> Best,
> Alfred
>

Alfred Rossi

unread,
Feb 21, 2010, 3:32:43 PM2/21/10
to v8-u...@googlegroups.com
You should hold your object in a weak persistent reference from the
start.

If I understand you correctly, I think what you actually want to do is
create a Persistent::External for holding your wrapped class pointer,
and call MakeWeak on that. The handler given to MakeWeak should cast the
pointer back to it's original type and delete it. The rest about
deleting after removal is automatic and happens as deemed necessary by
the garbage collector.

deadmorous

unread,
Feb 21, 2010, 4:54:35 PM2/21/10
to v8-users
Yes, exactly; I have smart pointers with reference counting in my app,
but this doesn't matter.

Actually, as wou warned, my callback hasn't been called in a small
example, even though the engine should have shut down (I called
Persistent<Context>::Dispose() for the context, and V8::Dispose()
afterwards). Still my handler has not been called. Well, if this is
just becasue GC hasn't worked a single time, I can of course kill all
of my instances after engine shutdown. Will also try running a more
memory intensive test script to find out what's going on...

deadmorous

unread,
Feb 21, 2010, 5:57:38 PM2/21/10
to v8-users
Strange, the handler isn't called at all. Here is the code of
constructor of example JS object:
---- 8< ----
v8::Handle<v8::Value> jsnative_myobj(const v8::Arguments& args) {
if( args.IsConstructCall() ) {
// Create object template here (well, this is just a test,
performance doesn't matter)
Handle<ObjectTemplate> objTpl = ObjectTemplate::New();
objTpl->SetInternalFieldCount( 1 );
objTpl->SetNamedPropertyHandler(
MyNamedPropertyGetter, MyNamedPropertySetter,
MyNamedPropertyQuery, MyNamedPropertyDeleter,
MyNamedPropertyEnumerator );

// Create new object instance
Local<v8::Object> obj = objTpl->NewInstance();

// Store an application-specific pointer in an internal field
as a persistent handle
Persistent< External > hm( External::New( new
map<string,string>() ) ); // Set handle data
hm.MakeWeak( 0, RemoveMyObj ); // Make the handle weak,
specify the callback
obj->SetInternalField( 0, hm ); // Store handle in internal
field

// Return instance just created, which should cause the
current This object to be discarded.
return obj;
}
else
return v8::Undefined();
}
---- 8< ----
The corresp. function object is created using a function template and
set as a global property:
---- 8< ----
Local<FunctionTemplate> f_myobj =
FunctionTemplate::New(jsnative_myobj);
f_myobj->SetClassName( String::New("MY_OBJ") );
context->Global()->Set( String::New("myobj"), f_myobj-
>GetFunction() );
---- 8< ----
Then I execute the following script:
---- 8< ----
for( i=1; i<1000000; ++i ) {
o = new myobj
if( i % 100 == 0 ) print( i );
o.p1 = "123";
o.p2 = i.toString();
}
---- 8< ----
I expect that GC would remove instances created in a loop, which
causes weak handles in internal fields of my instances to die, which
in turn causes the invocation of the RemoveMyObj() handle.
Unfortunally, this doesn't happen; task manager shows that memory
usage constantly grows. What am I doing wrong?

Regards,
Deadmorous

Alfred Rossi

unread,
Feb 21, 2010, 7:17:27 PM2/21/10
to v8-u...@googlegroups.com
This looks fine to me. The garbage collector may not have deemed it
worthwhile to collect for whatever reason.

One thing that looks wrong, unrelated to your question, is that you're
creating a new object template in a local handle and then returning it.
Once that Local handle goes out of scope I believe your object is
considered garbage.

I think v8 has already created an object for you to store your result in
and put it in args.Holder(); What you should do is create a
FunctionTemplate and setup the functiontemplate->InstanceTemplate() like
your objTpl below (give it the appropriate field count, property
handlers, etc). Use the function template instance's ->GetFunction() for
the function used for construction. Then your callhandler should just
store the persistent pointer to the c++ object in args.Holder(), and
return that.

As for why the garbage collector doesn't fire I have no idea. I am not
sure that it should be. Perhaps someone else could step in here?

Best,
Alfred

deadmorous

unread,
Feb 22, 2010, 4:31:10 AM2/22/10
to v8-users

On 22 фев, 03:17, Alfred Rossi <alfr...@gmail.com> wrote:
> This looks fine to me. The garbage collector may not have deemed it
> worthwhile to collect for whatever reason.

I have prepared a minimal example program, in which gc is forced
(using v8/gc extension). Still no effect. Here is the code:
---- 8< ----
// main.cpp

#pragma warning (disable:4251) // I'm on Windows and using v8 as a
dll, ang getting this warning under these conditions

#include <iostream>
#include "v8.h"

using namespace std;
using namespace v8;

Handle<ObjectTemplate> objTpl;

Handle<Value> jsnative_print(const Arguments& args) {
for (int i = 0; i < args.Length(); i++) {
HandleScope handle_scope;
if( i > 0 )
cout << ' ';
cout << *String::AsciiValue( args[i] );
}
cout << endl;
return Undefined();
}

void RemoveMyObj( Persistent<Value> object, void *parameter ) {
cout << "RemoveMyObj()\n";
}

/*
// Alternative version of object constructor, which returns weak
handle
// to new obejct itself (try adding a slash above to check it out)
Handle<Value> jsnative_myobj(const Arguments& args) {
if( args.IsConstructCall() ) {
Persistent<Object> obj( objTpl->NewInstance() );
obj.MakeWeak( 0, RemoveMyObj );
return obj;
}
else
return Undefined();
}
/*/
// Basic version of object constructor. Stores a weak handle
// in an internal field of the object
Handle<Value> jsnative_myobj(const Arguments& args) {
if( args.IsConstructCall() ) {
HandleScope handle_scope;
Local<Object> obj = objTpl->NewInstance();
Persistent< Number > hm = Persistent< Number
>::New( Number::New( 1 ) );
hm.MakeWeak( 0, RemoveMyObj );


obj->SetInternalField( 0, hm );

return handle_scope.Close( obj ); // At this point, only obj
local handle shoud survive
}
else
return Undefined();
}
//*/

int main( int argc, char **argv )
{
// Create a stack-allocated handle scope.
HandleScope handle_scope;

// Create a new context.
const char* extension_list[] = { "v8/gc" };
ExtensionConfiguration extensions(1, extension_list);
Persistent<Context> context = Context::New(&extensions);

// Enter the created context for compiling and
// running the hello world script.
Context::Scope context_scope(context);

// Create object template


objTpl = ObjectTemplate::New();
objTpl->SetInternalFieldCount( 1 );

// Add "print()" JS function
Local<FunctionTemplate> f_print =
FunctionTemplate::New(jsnative_print);
context->Global()->Set( String::New("print"), f_print-
>GetFunction() );

// Add my object constructor


Local<FunctionTemplate> f_myobj =
FunctionTemplate::New(jsnative_myobj);
f_myobj->SetClassName( String::New("MY_OBJ") );
context->Global()->Set( String::New("myobj"), f_myobj-
>GetFunction() );

// Run the script and get the result.
string scriptText =
"for( i=1; i<1000000; ++i ) {\n"
" o = new myobj\n"
" if( i % 100 == 0 )\n"
" { print( i ); gc(); }\n"
" o.p1 = '123';\n"
" o.p2 = i.toString();\n"
" }\n";
Handle<Value> result =
Script::Compile( String::New( scriptText.c_str() ) )->Run();
context.Dispose();
cout << *String::AsciiValue(result) << endl;
V8::Dispose();
return 0;
}
---- 8< ----


>
> One thing that looks wrong, unrelated to your question, is that you're
> creating a new object template in a local handle and then returning it.
> Once that Local handle goes out of scope I believe your object is
> considered garbage.

I suppose that the main property of local handles is that their
lifetime is determined by lifetime of the HandleScope instance on the
stack, and by corresponding scope. In our case, we have such an object
in main(); in last example, also in jsnative_myobj().

>
> I think v8 has already created an object for you to store your result in
> and put it in args.Holder(); What you should do is create a
> FunctionTemplate and setup the functiontemplate->InstanceTemplate() like
> your objTpl below (give it the appropriate field count, property
> handlers, etc). Use the function template instance's ->GetFunction() for
> the function used for construction. Then your callhandler should just
> store the persistent pointer to the c++ object in args.Holder(), and
> return that.

Do you mean that object being created should be written to
args.Holder()?
Unfortunately v8::Arguments is undocumented...

> As for why the garbage collector doesn't fire I have no idea. I am not
> sure that it should be. Perhaps someone else could step in here?

In new example, gc invocation is forced, and nothing still hapens.
Please help me someone! :)

Regards,
Deadmorous

deadmorous

unread,
Feb 22, 2010, 1:52:45 PM2/22/10
to v8-users
Finally I have found the solution.
1. The "alternative" version of constructor jsnative_myobj() should be
used (it is commented out in the code in my previous post above).
2. Persistent handles are created using Persistent::New(). So, the
line

Persistent<Object> obj( objTpl->NewInstance() );
in jsnative_myobj() has to be replaced with the following one:
Persistent<Object> obj = Persistent<Object>::New( objTpl-
>NewInstance() );
With these modifications, the callback RemoveMyObj() gets called at GC
time, so I can go further in using V8.

> ...
>
> продолжение »

Ryan Dahl

unread,
Feb 22, 2010, 2:38:30 PM2/22/10
to v8-u...@googlegroups.com
2010/2/22 deadmorous <deadm...@gmail.com>:

> Finally I have found the solution.
> 1. The "alternative" version of constructor jsnative_myobj() should be
> used (it is commented out in the code in my previous post above).
> 2. Persistent handles are created using Persistent::New(). So, the
> line
>    Persistent<Object> obj( objTpl->NewInstance() );
>    in jsnative_myobj() has to be replaced with the following one:
>    Persistent<Object> obj = Persistent<Object>::New( objTpl-
>>NewInstance() );
> With these modifications, the callback RemoveMyObj() gets called at GC
> time, so I can go further in using V8.

You might find this simple object wrapper useful - it will call the
c++ destructor when the object is GCed.
http://github.com/ry/node/blob/bb0d1e65e1671aaeb21fac186b066701da0bc33b/src/node_object_wrap.h
Here is an example usage:
http://github.com/ry/node/blob/bb0d1e65e1671aaeb21fac186b066701da0bc33b/src/node_idle_watcher.cc

Seiji Sam Lee

unread,
Feb 22, 2010, 3:53:41 PM2/22/10
to v8-u...@googlegroups.com
Hi, everybody:

I have a template as this:

GLOBAL
|
+---- A
|
+--- B
|
+--- C

Where A, B and C are ObjectTemplate

I can put a pointer (SetPointerInInternalField) in GLOBAL, but all my work
to put a pointer into **objects** a, b or c cause a segment fault.

This fault is done after context be created. All samples show how to put a
pointer into the GLOBAL object, but is not easily to deduce the way to put a
pointer in every object of the template.

Put a pointer into GLOBAL object is well documented an clear.

I need help :-(

Ondřej Žára

unread,
Feb 22, 2010, 3:56:32 PM2/22/10
to v8-u...@googlegroups.com
Have you also prepared some good amount of internal fields on your objects? (InstanceTemplate()->SetInternalFieldCount())



O.


2010/2/22 Seiji Sam Lee <seiji...@gmail.com>

Seiji Sam Lee

unread,
Feb 22, 2010, 3:59:44 PM2/22/10
to v8-u...@googlegroups.com

Using SetPointerInInternalField into GLOBAL object don’t need SetInternalFieldCount, at last it works for my!! By the way, I tried that and doesn’t work.

Степан Орлов

unread,
Feb 22, 2010, 8:03:18 PM2/22/10
to v8-u...@googlegroups.com
Thanks, Ryan,
think I will use your wrapper or something very similar.

Regards,
Deadmorous


2010/2/22 Ryan Dahl <coldre...@gmail.com>:

Reply all
Reply to author
Forward
0 new messages