57005 or alive

JNI object lifetimes - quick reference

Feb 1, 2016 Java JNI Scala

I’ve recently had reason to do a bit of work with JNI . ¬†Throughout the course of this work I had to do quite a lot of Googling in order to figure out how to properly manage the caching of various JNI objects used by my C++ code. Some JNI objects can be safely cached and re-used at any point, while others have limited lifetimes and require special handling. Obtaining JNI objects through JNI APIs is, broadly speaking, fairly expensive, so it’s smart to persist those objects which will be re-used in multiple places. You just need to be careful.

I expected to find a big table somewhere that documented the lifetime restrictions (or lack thereof) for each of the JNI object types, but sadly I was unable to locate one. Instead, I wound up trawling through numerous Stack Overflow replies, blogs, forums, and other documentation to obtain this information.

This post is my effort to provide to others the missing quick reference I wish I’d found. I recommend this Android documentation as further reading if you want more details.

JNI Object Use across JNI calls? Use on different thread? Notes
JavaVM Yes Yes To obtain an instance of JavaVM, either:
  • Expose a native export jint JNI_OnLoad(JavaVM* vm, void* reserved), which will be invoked when LoadLibrary is called from Java
  • Call pJNIEnv->GetJavaVM(&pJavaVM) on a valid JNIEnv
You don't need to clean up a JavaVM.
JNIEnv Yes No A JNIEnv is valid across JNI calls, but only on its original thread. If you are going to cache a JNIEnv, consider using a thread_local or similar.

If a JNIEnv is not available for the current thread (e.g. within a native callback), one can be obtained by calling pJavaVM->AttachCurrentThread(&pJNIEnv, null). In this case you are responsible for cleaning up with pJavaVM->DetachCurrentThread().

Otherwise, you don't need to clean up a JNIEnv.

Primitive value types

jboolean, jchar, jshort, jfloat, jdouble, jsize, jint, jlong, jbyte

Yes Yes These are just aliases for normal native value types, so there are no special lifetime or cleanup considerations.
Reference types

jobject, jclass, jthrowable, jstring, jarray, jbooleanArray, jbyteArray, jcharArray, jshortArray, jintArray, jlongArray, jfloatArray, jdoubleArray, jobjectarray

No (local refs) No (local refs) If one of these is passed in as an argument to a JNI call, or returned from another JNI API, it is typically a local reference and is only valid for the duration of that JNI call, on that same thread. These do not require any cleanup.

A durable global reference can be created from any local reference by calling globalJThing = (jthing) pJNIEnv->NewGlobalRef(localJThing). Global references are valid across JNI calls and on any thread. Global references must be cleaned up with pJNIEnv->DeleteGlobalRef(globalJThing).

Yes (global refs) Yes (global refs)
ID types

jfieldID, jmethodID

Yes Yes These are durable and can be used without restriction. No cleanup is required.