javacjava-native-interfaceprogramming-languagesjnienv

Why does the JNI C API use a pointer-to-pointer instead of straight pointers for JNIEnv?


Disclaimer: I have implemented my own object-oriented programming languages, so am familiar with (and have taught) pointers and C. Memory management 101 is not what this question is about. Neither is this about the JNI C++ API that looks similar. Thank you.

When you look at the JNI C API for Java, in jni.h, JNIEnv is a typedef for a pointer to a struct JNINativeInterface. Given all JNI C API takes a JNIEnv*, this means it is really a JNINativeInterface **, meaning a pointer to a pointer to a struct.

Why does JNI use that extra level of indirection?

Simulating an object-like construct in C would work just fine with a JNINativeInterface*. You could just call

env->NewGlobalRef(env, my_object);

so why force us to do

(*env)->NewGlobalRef(env, my_object);

? The extra level of indirection is passed into the functions as the first argument, so I guess they could update that pointer. Is that it?

Correction: I had originally mis-remembered and passed (*env) instead of env as the first argument and therefore excluded the possibility of the callee editing the pointer itself. I've corrected the post. Thanks to John Bollinger for pointing that out.


Solution

  • JNIEnv is not really a pointer to a pointer, but to a data structure containing other (private) thread-specific information. The JNINativeInterface* is just the first field in the struct, and the rest aren't public. This allows for more flexibility in VM's implementation of JNI function tables.

    Some links here for the benefit of those who might come across this:

    1. Threads and JNI - here it explains:

      The JNI interface pointer (JNIEnv *) is only valid in the current thread. You must not pass the interface pointer from one thread to another, or cache an interface pointer and use it in multiple threads. The Java Virtual Machine will pass you the same interface pointer in consecutive invocations of a native method from the same thread, but different threads pass different interface pointers to native methods.

    2. JNI spec