我正在尝试实现一个JNI库。我注意到,当从构造函数调用时传递给JNI函数的thisObj与从方法调用的相同函数不同。
下面是我的最小代码:
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);
}
}
我这样称呼它:
Test t = new Test();
t.m();
JNI端看起来像这样:
static void jni_test(JNIEnv *env, jobject thisObj, int i) {
printf("jni_test %i %p\n", i, thisObj);
}
输出为:
jni_test 0 0x7f3bb0288898
jni_test 1 0x7f3bb02888e0
如你所见,ThisObj不一样。更准确地说,thisObj 在构造函数中调用时,指的是Test类。当从方法调用时,它引用Test**的 * 示例。
这是为什么呢?
如何解决它(除了显式地将 this 作为一个参数传递给jni函数)?
2条答案
按热度按时间ff29svar1#
如你所见,ThisObj不一样。更准确地说,当从构造函数调用时,thisObj引用Test类。当从方法调用时,它引用Test的示例。
这是为什么呢?
你的理论不正确。在C代码中输出的是一个对象句柄,而不是
this
指针。指向java对象的指针不直接公开给本机代码。这样做是行不通的,因为垃圾收集器可以四处移动对象,即使在执行本机代码的同时也是如此。相反,VM将分配一个对象句柄,它可以被认为是间接引用Java对象的令牌,只有GC知道如何正确访问(由API封装)。在多个调用中,这意味着句柄的值可以更改,因为每个调用都会创建一个新句柄。
但在这两种情况下,句柄都将引用
this
对象。这与调用方法的位置无关。因为jni_test
是一个示例方法,所以它需要一个接收器参数。Java代码中对jni_test(...)
的两个调用都是this.jni_test(...)
的缩写。this
就是thisObj
句柄在本机代码中引用的内容。这在JNI规范中也有解释:
JNI接口指针是本机方法的第一个参数。JNI接口指针的类型为JNIEnv。第二个参数根据本机方法是静态的还是非静态的而有所不同。非静态本机方法的第二个参数是对对象的引用。静态本机方法的第二个参数是对其Java类的引用。
jni_test
方法不是static
,因此第二个参数引用对象。如何解决它(除了显式地将此作为一个参数传递给jni函数)?
我不知道你想达到什么目的,或者你在寻找什么样的解决方案。不过请放心,没有办法公开指向任意Java对象的稳定本机地址。
w1e3prcc2#
你将你的方法声明为
private native void jni_test(int i);
,这意味着对应的C签名是:不管它是从哪里调用的,
obj
是 * Test* 类的示例。您可以安全地将obj
传递给其他方法或调用obj
上的方法,但在构造函数中进行操作时也需要注意同样的问题。另一个答案已经解释了为什么你的测试是测试错误的东西,所以我不会重复它。
编辑:我写了下面的代码来反驳你的说法:在测试中添加了以下主要方法:
下面是jni_test方法中的:
其产生以下输出:
这最终证明了是的,即使在构造函数中,
this
也引用了正在构造的对象。我不知道你是如何得出不同的结论的,但我只能假设你的测试代码是错误的。