Elegant memory management with Kotlin and J2V8
Tags: android, kotlin, javascript
Published: 2019-10-29 17:22:52

At [YNAB](https://youneedabudget.com/) we have a cross-platform library written in TypeScript (compiled to JavaScript) which contains all of our shared business logic for Android, iOS and Web. 

On Android we use [J2V8](https://github.com/eclipsesource/J2V8) as our bridge into the JavaScript world; it's a nice Java wrapper around [Google's V8 JavaScript engine](https://v8.dev/). It works beautifully, but one of the challenges it brings is memory management. It's so tricky that the J2V8 maintainers wrote [a blog post about it](https://eclipsesource.com/blogs/2016/07/29/improved-memory-management-with-j2v8/). 

> 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...

To cut a long story short, we have to explicitly release any JS objects we create in our Java/Kotlin code. 

## Remember to close the door

We can release these objects manually:

```kotlin
// create a JS object
val obj = V8Object(v8)

// do something with the object in JS land
obj.add("someProperty", 54321)
obj.executeJSFunction("someJSFunction", 42)

// now release it
obj.close()
```

But it's a bit of a pain having to remember to call `close()` for every object. `V8Object` implements the `Closeable` interface which means we can use Java's try-with-resources or Kotlin's `use { }` to take care of cleanup where we only have a single object to deal with.

```kotlin
V8Object(v8).use { obj ->
    obj.add("someProperty", 54321)
    obj.executeJSFunction("someJSFunction", 42)
}
```

It gets hairy when we need to track multiple JS objects, though. To help, J2V8 provides a `MemoryManager`. When we create one of these it starts tracking `V8Object` allocations while it is open, and releasing the `MemoryManager` causes all of those objects which were allocated during its lifetime to be released in turn.

```kotlin
val manager = MemoryManager(v8)

val obj1 = V8Object(v8)
val obj2 = V8Object(v8)

obj1.add("someProperty", 54321)
obj2.executeJSFunction("someJSFunction", 42)

manager.release()   // obj1 and obj2 are both released
```

It would be nice if we could use try-with-resources or `use { }` again here, to avoid the explicit call to `manager.release()`, but `MemoryManager` doesn't implement `Closeable` so we can't.

## An ~~elephant~~ elegant solution

What we can do, though, is add a helper function which wraps all the `MemoryManager` stuff and provides a scope for allocating and safely cleaning up as many `V8Object`s as we like.


```kotlin
inline fun <T> V8.scope(body: () -> T) : T {
    val scope = MemoryManager(this)
    try {
        return body()
    } finally {
        scope.release()
    }
}
```

It has to be `inline` so that we don't interfere with any return value from the `body` lambda. And making it an extension function on `V8` gives us this concise and elegant syntax.


```kotlin
v8.scope {
    val obj1 = V8Object(v8)
    val obj2 = V8Object(v8)

    obj1.add("someProperty", 54321)
    obj2.executeJSFunction("someJSFunction", 42)
}   // obj1 and obj2 are both released
```

Elephants never forget... and now, neither do we! This approach helps us to solve some of the memory-related pain points when mixing JavaScript and good old Java/Kotlin code, without too much boilerplate. We do have a keen eye on Kotlin multiplatform for the future, but our JavaScript shared library is serving us very nicely in the meantime.

The code is available on [GitHub](https://gist.github.com/GrahamBorland/f4b9e92ed0df2b0fbe2b1e8453c4f4ed).