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.
<pre lang="java">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(); </pre>
Using the new MemoryManager, API in this code is greatly simplified.
<pre lang="java">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(); </pre>
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.