JNI构造函数调用native方法时thisObj指向类而非实例的原因与解决
构造函数中JNI调用的thisObj与普通方法不同的原因及解决办法
原因分析
在Java对象的构造函数执行期间,实例对象尚未完全初始化完成。此时调用native方法时,JVM不会传递正在创建的实例引用,而是传递该类的Class对象(即Test.class)作为thisObj参数。这是JVM的安全机制,防止native代码访问未初始化的实例数据,避免不可预期的错误。你看到的两个不同地址,正是Test.class对象和最终创建的Test实例对象的内存地址。
解决办法
除了显式将this作为额外参数传入JNI函数外,还有以下两种可靠方案:
1. 延迟native方法调用至实例初始化完成后
避免在构造函数中直接调用native方法,改用静态工厂方法创建实例,在实例完全初始化后再执行native调用:
public final class Test { static { System.loadLibrary("jnitest"); } private native void jni_test(int i); // 私有构造,禁止外部直接创建 private Test() {} public static Test createInstance() { Test instance = new Test(); // 实例已完全初始化,此时调用native方法会传递实例引用 instance.jni_test(0); return instance; } public void m() { jni_test(1); } }
调用方式改为:
Test t = Test.createInstance(); t.m();
2. 在JNI层转换类对象为实例对象(不推荐)
如果必须在构造函数中调用native方法,可以在JNI层通过反射尝试获取正在初始化的实例,但这种方法依赖JVM内部实现,兼容性差,不建议用于生产环境。示例代码(仅作演示):
#include <jni.h> #include <stdio.h> static void jni_test(JNIEnv *env, jobject thisObj, int i) { jclass cls = (*env)->GetObjectClass(env, thisObj); // 判断传入的是否是Class对象 jclass classCls = (*env)->FindClass(env, "java/lang/Class"); if ((*env)->IsInstanceOf(env, thisObj, classCls)) { // 尝试获取构造过程中的实例(依赖JVM隐式字段,不同版本可能失效) jfieldID instanceField = (*env)->GetStaticFieldID(env, cls, "this$0", "LTest;"); jobject realInstance = (*env)->GetStaticObjectField(env, cls, instanceField); printf("jni_test %i %p (real instance: %p)\n", i, thisObj, realInstance); } else { printf("jni_test %i %p\n", i, thisObj); } (*env)->DeleteLocalRef(env, classCls); }
内容的提问来源于stack exchange,提问作者PoltoS
相关产品推荐
相关产品推荐

