Re: [v8-users] Destructor in V8

948 views
Skip to first unread message
Message has been deleted

Mads Sig Ager

unread,
Feb 16, 2009, 3:06:24 AM2/16/09
to v8-u...@googlegroups.com
From C++ you can create a Persistent handle for your object and make
the persistent handle weak (by calling the MakeWeak function). When
making a persistent handle weak, you can provide a callback function
that will be called when there are no more non-weak references to the
object and when there are no more references to it in JavaScript (at
which point you should dispose your persistent handle).

Please note that the callback is not going to be called immediately
when there are no more references to the object. Weak handles are
only processed on global garbage collections, so it might take a while
before the callback is invoked.

-- Mads

On Mon, Feb 16, 2009 at 6:02 AM, <t2k...@gmail.com> wrote:
>
> Hi all,
>
> I am using V8 engine to develop a Windows Application. I created a set
> of ObjectTemplate for my JS to use. For example:
>
> var img = new Image("aaa.png");
> ....
> img.dispose();
>
> My question is: Can I define a handler that will be invoked when the
> engine is Garbage Collecting the variable img? It is just like a
> destructor. So that I needn't dispose the created object explicitly.
>
> Regards,
> Thomas
> >
>

t2k...@gmail.com

unread,
Feb 17, 2009, 9:39:05 AM2/17/09
to v8-users
Hi Mads,

Thanks for your reply.

From your hints, I found the following article:

http://create.tpsitulsa.com/blog/2009/01/31/persistent-handles/

The content of that should be the same as your meaning and I have
implemented the following code:

typedef void (*DestructorCallBack)(v8::Handle<v8::Object> object);

struct __DESTRUCTOR_S
{
DestructorCallBack cb;
v8::Persistent<v8::Object> ref;
};

static void __DestructorCallback(v8::Persistent<v8::Value> ref, void*
parameter)
{
v8::HandleScope handle_scope;

__DESTRUCTOR_S *pds = (__DESTRUCTOR_S *)parameter;
pds->cb(v8::Handle<v8::Object>::Cast(ref));
delete pds;
}

void SetDestructor(v8::Handle<v8::Object> self, DestructorCallBack cb)
{
__DESTRUCTOR_S *pds = new __DESTRUCTOR_S();
pds->cb = cb;
pds->ref = v8::Persistent<v8::Object>::New(self);
pds->ref.MakeWeak(pds, __DestructorCallback);
}

And in the constructor, just call " SetDestructor(self, <a callback to
destroy the internal fields>);" to setup a destructor.

In the code, I used a structure to store a callback and a
PersistentHandle and pass it to the Weak Reference callback. In the
generic __DestructorCallback, it calls the callback stored in the
structure and delete the structure. Is it correct? and do I need to
destroy the Persistent Handle explicitly?

Regards,
Thomas

Alex Iskander

unread,
Feb 17, 2009, 9:52:34 AM2/17/09
to v8-u...@googlegroups.com
It looks correct, except that you do need to dispose the handle. Apparently I forgot to add that in my article (I have fixed this now). 

I think you need to add the following lines to __DestructorCallback:
ref.Dispose();
ref.Clear();

Sorry for the confusion,

Alex

Alex Iskander
Web and Marketing
TPSi



t2k...@gmail.com

unread,
Feb 17, 2009, 8:15:03 PM2/17/09
to v8-users
Hi Alex,

Your article is great. It helps me to understand V8 engine more
deeply. I will try to Dispose and Close the my persistent handler in
my code.

Thanks!
Regards,
Thomas

t2k...@gmail.com

unread,
Feb 17, 2009, 8:34:03 PM2/17/09
to v8-users
I have revised my code and try to created many and many "Image" object
in my javascript code just like:

var img;
for (var i = 0; i < 1000; i++) {
img = new Image('aaa.png');
sleep(500); // This function is written by me for waiting
}

but my destructor wasn't run at all. I saw that Mads said that "Weak
handles are only processed on global garbage collections". How to let
the engine to perform global GC?

In my situation, I am developing an application with very few memory
(Just 5-10MB). The garbage(s) were not collected in minute is not
acceptable (in second is good). I have tried to use command line
option --expose_gc=true to add the "gc();" method to my javascript
code and call it periodically, but nothing happened. I found that many
and many people say many terms, such as "old space" and "young space",
what is that? And I also found an internal method "CollectAllGarbage"
in V8. Should I export the "CollectAllGarbage" for using in my code?

Regards,
Thomas

Alex Iskander

unread,
Feb 17, 2009, 8:46:58 PM2/17/09
to v8-u...@googlegroups.com
First, the garbage collection isn't done so much based on time. Even if it was, v8 is not multithreaded, so  calling "sleep" would stall garbage collection as well.

Garbage collection seems to occur when V8 thinks it needs to free some memory. If it thinks it is using barely any memory, it will not collect the garbage. However, you can "trick" V8 into garbage collecting more often by making it think it is using more memory: you use AdjustAmountOfExternalAllocatedMemory. Through that you can make garbage collection seem more urgent to V8 by telling it there is a lot of extra allocated memory.

For instance, on construct:
V8::AdjustAmountOfExternalAllocatedMemory(1024);

On destruct:
V8::AdjustAmountOfExternalAllocatedMemory(-1024);


This is usually used with the size of the actual object, but you can multiply the size if you want v8 to garbage collect more often.

If you would like to test to make sure your destructor is working, a better function would be:

 var img;
 for (var i = 0; i < 100000; i++) {

   img = new Image('aaa.png');
 }

Finally, it is probably not safe to use CollectAllGarbage, as it is internal.

Alex

t2k...@gmail.com

unread,
Feb 17, 2009, 11:34:21 PM2/17/09
to v8-users
Hi Alex,

Thanks for your fast reply.

In fact, I have looked at all exported functions/methods in v8
namespace for forcing the engine to do GC just in my expectation. I
saw AdjustAmountOfExternalAllocatedMemory for a long time but don't
know it's usage. May I think the usage of that function is:

For example:

var img = new Image("aaa.png");

Since the class Image is a wrapper wrapped C++ object(s), assume
that V8 engine requires 256bytes to store the new object instance
(this should not be cared by V8) and I should tell v8 how many extra
memory used by the C++ object. So, if the size of the image is approx.
200K, the extra memory usage of the object instance is 204,800 bytes.
So, I should call below code in the constructor:

v8::AdjustAmountOfExternalAllocatedMemory(204800); // The number
is based on the size of the image.

And in the destructor of Image, adjust it again by

v8::AdjustAmountOfExternalAllocatedMemory(-204800);

Is it correct?

Regards,
Thomas

Alex Iskander

unread,
Feb 17, 2009, 11:43:51 PM2/17/09
to v8-u...@googlegroups.com
That is correct. And when dealing with images, you most certainly will
want to use this functionality.

In my application, my objects are mostly relatively small, yet I need
garbage collection to run often when debugging memory leaks. In these
instances, I fudge the numbers a bit and multiply by 1000. But when
your objects are 204800 bytes, I think V8 will already feel compelled
to garbage collect often, and you probably won't need to fudge the
numbers.

Alex

t2k...@gmail.com

unread,
Feb 17, 2009, 11:51:26 PM2/17/09
to v8-users
OK. I will try this tonight. Thank you very much for your information.

Regards,
Thomas

Alex Iskander

unread,
Feb 17, 2009, 11:55:47 PM2/17/09
to v8-u...@googlegroups.com
You're welcome. I'm glad to help.

Something I didn't catch earlier: please note that V8 is in caps here:
the namespace name is v8, lower case, but the V8 management static
class is capitalized.

It should read:
V8::AdjustAmountOfExternalAllocatedMemory(204800); // The number

Alex

Alex Iskander

unread,
Feb 18, 2009, 12:16:08 AM2/18/09
to v8-u...@googlegroups.com
I've written an article on this (garbage collecting) in our wiki:

I'm considering working on parts of a "v8 Cookbook" as has been discussed here before. I'm posting it on our wiki, as it seems to be a more proper place than the blog.

Is anyone interested?

Also, I'd be willing to take content from other people (with proper credit, of course). Just post it here on the discussion board and I'd be glad to add it (although, I warn, I might edit it).

Alex

t2k...@gmail.com

unread,
Feb 18, 2009, 1:51:03 AM2/18/09
to v8-users
Oh!! Its great.

I will post questions here if I hit any problem in development.

Thank you very much.
Regards,
Thomas
> ...
>
> read more >>

t2k...@gmail.com

unread,
Feb 19, 2009, 4:42:12 AM2/19/09
to v8-users
Hi Alex,

The method V8::AdjustAmountOfExternalAllocatedMemory works perfectly
for my application. V8 engine can perform GC randomly now. Thanks for
your kind help.

In addition, I have a new idea. If there is an application which uses
a lot of C++ objects, the ratio of memory used by V8 internal context
and the memory usage of the current process is very small. May I
adjust the external allocated memory by using the memory usage of the
current process? Is it meaningful for V8?

For example:
Assume that I have a function called "GetMemoryUsage" which returns
the number of bytes allocated by the current process. So, I
periodically do the following:

V8::AdjustAmountOfExternalAllocatedMemory(GetMemoryUsage() -
V8::AdjustAmountOfExternalAllocatedMemory(0));

This can threat all memory usage of the current process as global, so
if C++ used too much memory, V8 may try to GC to free more memory for
using by C++.

Regards,
Thomas

Alex Iskander

unread,
Feb 19, 2009, 8:24:01 AM2/19/09
to v8-u...@googlegroups.com
You're welcome.

I can't see any problem with using AdjustAmountOfExternalAllocatedMemory that way. From what I know, it should work.

The only concern is that you would be including memory allocated by V8 in that amount. If v8 had some really strange behavior, like increasing its own amount of memory each time you called AdjustAmountOfExternalAllocatedMemory, then that number could end up auto-growing. I don't think v8 would do that, however, so you should be safe.

Finally, there is something I found in v8.h that may help, but they are not all that thoroughly documented, so I am not certain they'd work how I expect:

/**
* A set of constraints that specifies the limits of the runtime's
* memory use.
*/
class EXPORT ResourceConstraints {
public:
ResourceConstraints();
int max_young_space_size() const { return max_young_space_size_; }
void set_max_young_space_size(int value) { max_young_space_size_ = value; }
int max_old_space_size() const { return max_old_space_size_; }
void set_max_old_space_size(int value) { max_old_space_size_ = value; }
uint32_t* stack_limit() const { return stack_limit_; }
void set_stack_limit(uint32_t* value) { stack_limit_ = value; }
private:
int max_young_space_size_;
int max_old_space_size_;
uint32_t* stack_limit_;
};


bool SetResourceConstraints(ResourceConstraints* constraints);

I am guessing that you can create one of these ResourceConstraints objects, and call v8::SetResourceConstraints using it. I'm not sure how it would affect v8, though. It is used in one of v8's tests like this:

TEST(OutOfMemory) {
  // It's not possible to read a snapshot into a heap with different dimensions.
  if (v8::internal::Snapshot::IsEnabled()) return;
  // Set heap limits.
  static const int K = 1024;
  v8::ResourceConstraints constraints;
  constraints.set_max_young_space_size(256 * K);
  constraints.set_max_old_space_size(4 * K * K);
  v8::SetResourceConstraints(&constraints);

  // Execute a script that causes out of memory.
  v8::HandleScope scope;
  LocalContext context;
  v8::V8::IgnoreOutOfMemoryException();
  Local<Script> script =
      Script::Compile(String::New(js_code_causing_out_of_memory));
  Local<Value> result = script->Run();

  // Check for out of memory state.
  CHECK(result.IsEmpty());
  CHECK(context->HasOutOfMemoryException());
}


Alex
Reply all
Reply to author
Forward
0 new messages