You probably don’t like memory leaks. I don’t. But, if you have one object that needs to pass between two languages — C++ and JavaScript — each of which may keep that object indefinitely, it can become tricky to make sure that any given object is destroyed, and that it is destroyed at the right time.
There are two things that can happen: an object can be deleted two soon, while it is still being used elsewhere — causing some particularly delicious crashes — or, an object can be deleted too late (or not at all), causing memory leaks.
Consider the point wrapping function from my previous post:
Handle<Object> wrapPoint(Point *pointToWrap) { //enter a handle scope HandleScope handle_scope; //create a new point instance Local<Object> point_instance = point_templ->NewInstance(); //set that internal field point_instance->SetInternalField(0, External::New(pointToWrap)); //to prevent the point_instance from being destroyed when its //scope handle_scope is, use the Close() function return handle_scope.Close(point_instance); }
What would happen if you passed a point wrapped using this function to JavaScript, and whatever JavaScript code you had decided to store it? It would then be a very bad idea to destroy it from C++: JavaScript would not know it was destroyed. If, for example, an accessor was called, it would call the C++ function corresponding to the accessor, and that C++ function would try to operate on the pointer stored in the internal field — the pointer to the point you destroyed.
There are a few possible solutions to the problem:
- Let JavaScript destroy it. For this, you need to know when JavaScript is no longer using the object. Thankfully, this is possible.
- Have C++ alert JavaScript to the destruction. For this, you’d have to keep track of the JavaScript object, which means you’d have to keep an active reference to it. As normal local handles get destroyed when their context scope goes away, you need a way to make sure the object sticks around in JavaScript, or a way to know that if it is no longer referenced in JavaScript, or both.
- Use smart pointers. In this case, you might store a pointer to a smart pointer to the object in the internal field. You’d still need to know when the object was no longer being used in JavaScript so you could get rid of that smart pointer, but in this case, the object will stick around so long as there is a reference to it from either C++ or JavaScript.
- Hybrid. Perhaps you will need to have the ability to keep objects alive by referencing them from JavaScript, but there are some situations in which you simply want to get rid of them (and JavaScript should no longer be able to access them). In this case, you need to keep track of the objects and use a smart-pointer like methodology. I won’t go into this, as it is (at least the method I use) a bit too tedious to go over in detail.
Note: Keeping track of the wrapped C++ objects in JavaScript can be useful for reasons other than memory management. The technique (which is also used in Google’s Chrome browser) also allows you to generate and reuse a single wrapper for any given object. In addition to being more memory efficient, this also allows JavaScript to add things to the object that will be remembered even if it jumps between languages a few times.
All of these techniques have one thing in common: they require the use of persistent handles.
Persistent Handles
A persistent handle is much like a C++ pointer:
//something like Value *value = new String("Hello, World"); Persistent<Value> value = Persistent<Value>::New(String::New("Hello, World")); //something like delete value value.Dispose(); //something like value = NULL; value.Clear();
Persistent handles seem to be a bit like a handle pointing to a handle. The handle being pointed to will not be destroyed until it is specifically told to.
For instance, the following will cause memory leaks:
//something like Value *value = new String("Hello, World"); Persistent<Value> value = Persistent<Value>::New(String::New("Hello, World")); //like setting value = new String("Hello, World 2"); Where does the previous string go? //answer: it floats. value = Persistent<Value>::New(String::New("Hello, World 2")); //and what about if we don't dispose of our persistent pointer //if we don't do the equivalent of calling "delete value;" return NULL;
You might be able to see how this could be used to keep a reference in a map between C++ objects and JavaScript objects, so that you could clear the JavaScript object’s internal field:
std::map<Point *, Persistent<Object> > jsPoints;
And then you could force the deletion of one something like this:
void destroyJSPointReference(Point *point) { //get the corresponding object Persistent<Object> object = jsPoints[point]; //clear internal field object->SetInternalField(0, NULL); //dispose and clear so that it will go away when nothing points to it object.Dispose(); object.Clear(); }
Weak Handles
But what if we wanted to know when JavaScript no longer uses an object? For that, we make the persistent handle weak. A weak persistent handle triggers a callback function when only weak handles point at an object. So, an adjusted wrapping function might look like this:
Handle<Object> wrapPoint(Point *pointToWrap) { //enter a handle scope HandleScope handle_scope; //store the wrappers in a map so that we don't double-delete. It's like double-dipping, //but seven times worse. if (jsPoints.count(pointToWrap) > 0) return jsPoints[pointToWrap]; //create a new point instance and point to it with a persistent handle Persistent<Object> point_instance = Persistent<Object>::New(point_templ->NewInstance()); //make the handle weak, with a callback point_instance.MakeWeak(pointToWrap, weakPointCallback); //add it to the map jsPoints[pointToWrap] = point_instance; //set that internal field point_instance->SetInternalField(0, External::New(pointToWrap)); //to prevent the point_instance from being destroyed when its //scope handle_scope is, use the Close() function return handle_scope.Close(point_instance); } void weakPointCallback(Persistent<Value> object, void *parameter) { Point *point = static_cast<Point *> (parameter); //remove it from the map jsPoints.erase(point); //javascript no longer uses the object //if we are certain C++ isn't using it, we could "Let JavaScript destroy it": delete point; //clear the reference to it object.Dispose(); object.Clear(); }
Now, using smart pointers in the mix is relatively easy, but will depend on what kind of smart pointer methodology you use — my example may not apply to you. If you really really don’t want to use smart pointers, you can still achieve a bit of the same effect — although I don’t recommend it. Consider:
std::set<Point *> usedInCPP; std::set<Point *> usedInJS; void JSDeletePoint(Point *point) { usedInJS.erase(point); if (usedInCPP.count(point) == 0) delete point; } void CPPDeletePoint(Point *point) { usedInCPP.erase(point); if (usedInJS.count(point) == 0) delete point; }
Something like this could allow you to keep track of references.
Using smart pointers is much easier. Note: my smart pointer type is named R:
Handle<Object> wrapPoint(R<Point> pointToWrap) { //enter a handle scope HandleScope handle_scope; //store the wrappers in a map so that we have only one wrapper per object if (jsPoints.count(pointToWrap) > 0) return jsPoints[pointToWrap]; //create a pointer to a smart pointer by making a new smart pointer R<Point> *point = new R<Point> (pointToWrap); //create a new point instance and point to it with a persistent handle Persistent<Object> point_instance = Persistent<Object>::New(point_templ->NewInstance()); //make the handle weak, with a callback point_instance.MakeWeak(point, weakPointCallback); //add it to the map jsPoints[pointToWrap] = point_instance; //set that internal field to the pointer to the smart pointer point_instance->SetInternalField(0, External::New(point)); //to prevent the point_instance from being destroyed when its //scope handle_scope is, use the Close() function return handle_scope.Close(point_instance); } void weakPointCallback(Persistent<Value> object, void *parameter) { R<Point> *point = static_cast<R<Point> *> (parameter); //remove it from the map jsPoints.erase(*point); //delete the smart pointer delete point; //clear the reference to it object.Dispose(); object.Clear(); }
That’s roughly what I do in the script engine, but I also have the ability to force the deletion of an object entirely. I do this by not actually giving a pointer, or a pointer to a smart pointer, to JavaScript at all, but instead giving it a smart pointer to a class which wraps the object. This wrapper can then be alerted when the object is destroyed, and its reference to the object will then be cleared.
I hope some people will find this helpful — I would have liked to have it when I was starting out with V8 a couple of weeks ago.
Please feel free to correct me or ask questions if you see anything fishy or strange.
Update: I realized I was forgetting to dispose of the persistent handle in the two examples above (lines object.Dispose and object.Clear). I have corrected this.