Ian is an Eclipse committer and EclipseSource Distinguished Engineer with a passion for developer productivity.
He leads the J2V8 project and has served on several …
Memory management in J2V8 has never been easy. Because J2V8 bridges V8 and Java, three different memory models are in play. Both Java and JavaScript provide a managed memory model with their own GC. JNI / C++ which sits in the middle is completely unmanaged. This leads to a complex situation since both JavaScript and Java will free memory if they think it’s unused, but have no knowledge of each other. To manage this, J2V8 puts the onus on the Java developer to specify which objects should be kept and which objects can be released.
However, remembering to release all V8Objects is both error prone and tedious. With J2V8 4.5 a new MemoryManager has been introduced.
Once instantiated, the MemoryManager is considered Active and will track all V8Objects. When the MemoryManager is released, all tracked objects will also be released. As a developer, you can create an instance of the MemoryManager at the beginning of a block, perform several J2V8 calls, and finally release the MemoryManager at the end of the block. Following this pattern, you can be ensured that no memory was leaked during the execution of that block.
Below is a simple example that uses Node.js and lodash to manipulate some objects. In this example, each object has to be tracked and released manually. This results in very verbose code since APIs cannot be chained.
loDash = nodeJS.require(new File("/Users/irbull/node_modules/lodash"));
V8Object o1 = o("a", 1);
V8Object o2 = o("b", 2);
V8Object o3 = o("c", 3);
V8Object objects = (V8Object) loDash.executeJSFunction("assign", o1, o2, o3);
LoDashObject e1 = loDash(objects);
LoDashObject e2 = e1.e("values");
V8Function f = f((V8Object receiver, V8Array parameters) -> parameters.getInteger(0) * 3);
LoDashObject result = e2.e("map",f);
System.out.println(result);
loDash.release();
e1.release();
e2.release();
f.release();
o1.release();
o2.release();
o3.release();
result.release();
objects.release();
Using the new MemoryManager, API in this code is greatly simplified.
MemoryManager scope = new MemoryManager(v8);
loDash = nodeJS.require(new File("/Users/irbull/node_modules/lodash"));
V8Object objects = (V8Object) loDash.executeJSFunction("assign", o("a", 1), o("b", 2), o("c", 3));
LoDashObject result = loDash(objects).e("values").e("map",
f((V8Object receiver, V8Array parameters) -> parameters.getInteger(0) * 3));
System.out.println(result);
scope.release();
In this example, a MemoryManager was created at the beginning of the block. All calls to lodash could be performed inline, and all API calls are chained. Finally, the MemoryManager is released, and all V8 handles that were allocated during that block are freed.
If you wish to keep some objects for future use, MemoryManager provides a persist API. We could use this on the result object for example.
MemoryManagers can also be nested. As you enter and leave new scopes, you could create a new MemoryManager to manage the objects within that scope. All previous MemoryManagers will get notified of all new object creations and deletions.
Grab J2V8 4.5 and try this out. For more J2V8 Tips and Tricks, follow me on Twitter.
Ian is an Eclipse committer and EclipseSource Distinguished Engineer with a passion for developer productivity.
He leads the J2V8 project and has served on several …