jni-为什么rt.jar system.load不工作,但是 Package 的方法工作?

btqmn9zl  于 2021-07-12  发布在  Java
关注(0)|答案(1)|浏览(287)

下面是一个演示(我省略了utils,它们只是检查是否存在异常并打印消息):
首先,它应该起作用:
c++部分:

jclass jClass_java_lang_System = env->FindClass("java/lang/System");
jmethodID jMethodID_java_lang_System_load = env->GetStaticMethodID(jClass_java_lang_System, "load", "(Ljava/lang/String;)V");
env->CallStaticVoidMethod(jClass_java_lang_System, jMethodID_java_lang_System_load, library_path);

没有例外,但是在那之后,调用一个本机方法 UnsatisfiedLinkError .
第二次尝试,编写一个 Package 器方法:

public static void load(String path) {
    System.load(path);
}

从c打过来++

jmethodID jMethodID_Driver_load = env->GetStaticMethodID(jClass_Driver, "load", "(Ljava/lang/String;)V");
env->CallStaticVoidMethod(jClass_Driver, jMethodID_Driver_load, library_path);
checkException(env);

它只是system.load的 Package 器,其他什么都没有,工作正常。本机呼叫工作正常。
如果要进行更多测试,但没有任何意义,请同时使用这两种方法:

jclass jClass_java_lang_System = env->FindClass("java/lang/System");
jmethodID jMethodID_java_lang_System_load = env->GetStaticMethodID(jClass_java_lang_System, "load", "(Ljava/lang/String;)V");
env->CallStaticVoidMethod(jClass_java_lang_System, jMethodID_java_lang_System_load, library_path);
if (!checkException(env)) std::cout << "Load by rt.jar no Exception" << std::endl;

jclass jClass_Driver = env->FindClass("Driver");
jmethodID jMethodID_Driver_load = env->GetStaticMethodID(jClass_Driver, "load", "(Ljava/lang/String;)V");
env->CallStaticVoidMethod(jClass_Driver, jMethodID_Driver_load, library_path);
checkException(env); // The first UnsatisfiedLinkError print by this util

// Second UnsatisfiedLinkError print by native method call, I omit it.

得到这个结果:

Load by rt.jar no Exception
java.lang.UnsatisfiedLinkError: Native Library XXXXX already loaded in another classloader
java.lang.UnsatisfiedLinkError: XXXXXXX

这使它更混乱,第一次尝试显示加载方式 java.lang.System-load() 不工作,但实际上已加载库。然后抛出重复加载异常。
然后倒序:

jclass jClass_Driver = env->FindClass("Driver");
jmethodID jMethodID_Driver_load = env->GetStaticMethodID(jClass_Driver, "load", "(Ljava/lang/String;)V");
env->CallStaticVoidMethod(jClass_Driver, jMethodID_Driver_load, library_path);
checkException(env);

jclass jClass_java_lang_System = env->FindClass("java/lang/System");
jmethodID jMethodID_java_lang_System_load = env->GetStaticMethodID(jClass_java_lang_System, "load", "(Ljava/lang/String;)V");
env->CallStaticVoidMethod(jClass_java_lang_System, jMethodID_java_lang_System_load, library_path);
if (!checkException(env)) std::cout << "Load by rt.jar no Exception" << std::endl;

结果是:

Load by wrapper no Exception
java.lang.UnsatisfiedLinkError: Native Library XXXX already loaded in another classloader
Result is - 2468
Result is - 2468

即使抛出重复加载异常,本机调用也能正常工作。
问题:发生了什么?如何解决?

tsm1rwdh

tsm1rwdh1#

加载本机库时 System.load() vm将尝试将找到的任何jni函数绑定到它们的java对应函数,即声明本机方法的类。它只能在该类已加载时执行此操作。如果您随后加载类,您将拥有未绑定的本机方法,并且当您调用它们时,您将得到一个 UnsatisfiedLinkError .
为了能够调用 Package 器方法,必须加载类,因此vm可以绑定本机方法。只需要打个电话 System.load() ,请确保vm已具有该类。也就是说,最好使用从类本身的静态初始值设定项加载本机库的常规方法。 loadLibrary 还会找到静态链接的库。因此,如果您将jni函数与代码的其余部分分离,并将它们放入它们自己的库中,您就可以静态地链接它并使用它 loadLibrary 名字很简单。

相关问题