I'm trying to implement a JNI library. I've noticed that thisObj passed to JNI function when called from a constructor differs from the same function called from a method.
Here is my minimal code:
public final class Test {
static {
System.loadLibrary("jnitest");
}
private native void jni_test(int i);
public Test() {
jni_test(0);
}
public void m() {
jni_test(1);
}
}
I call it like this:
Test t = new Test();
t.m();
JNI side looks like this:
static void jni_test(JNIEnv *env, jobject thisObj, int i) {
printf("jni_test %i %p\n", i, thisObj);
}
The output is:
jni_test 0 0x7f3bb0288898
jni_test 1 0x7f3bb02888e0
As you see, thisObj is not the same. To be more precise, thisObj refers to the Test class when called from the constructor. And it refers to the instance of the Test when called from a method.
Why is this?
How to workaround it (except from explicitly passing this as one more parameter to the jni function)?
You are incorrect in your theory. What you're printing out in the C code is an object handle, not the
this
pointer. Pointers to java objects are not exposed directly to native code. This wouldn't work as the garbage collector can move the objects around, even at the same time that the native code is executed.Instead the VM will allocate an object handle, which can be thought of as a token that indirectly refers to the Java object, and only the GC knows how to access correctly (which is encapsulated by the JNI api). Across multiple calls, this means that the value of the handle can change, because a new handle is created for every call.
But in both cases, the handle will refer to the
this
object. This has nothing to do with the place from where the method is called. Becausejni_test
is an instance method, it requires a receiver argument. Both of the calls tojni_test(...)
in your Java code, are just short forthis.jni_test(...)
. And thatthis
is what thethisObj
handle refers to in the native code.This is also explained in the JNI specification:
The
jni_test
method is notstatic
, so the second argument refers to the object.I'm not sure what you're trying to achieve, or what kind of workaround you're looking for. Rest assured though, that there's no way to expose a stable native address pointing at an arbitrary Java object.