JNI-为什么rt.jar System.load不起作用,但包装方法起作用?

问题描述

这是一个演示(我省略了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

第二次尝试,编写一个包装方法

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

并从C ++调用

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

它只是System.load的包装器,仅此而已,工作正常。本地呼叫正常工作。

然后进行更多测试,但没有任何意义-都使用它们:

jclass jClass_java_lang_System = env->FindClass("java/lang/System");
jmethodID jMethodID_java_lang_System_load = env->GetStaticmethodID(jClass_java_lang_System,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,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()的第一次尝试show加载不起作用,但实际上是在加载库。然后抛出一个重复的加载异常。

并颠倒顺序:

jclass jClass_Driver = env->FindClass("Driver");
jmethodID jMethodID_Driver_load = env->GetStaticmethodID(jClass_Driver,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,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

即使抛出重复的加载异常,本地调用也可以正常工作。

问题:会发生什么?该怎么解决

解决方法

当您使用System.load()加载本机库时,VM会尝试将其找到的任何JNI函数绑定到其Java对应的JNI函数,即声明本机方法的类。仅当该类已加载时,它才能这样做。如果事后加载该类,则将具有未绑定的本机方法,并且在调用它们时会得到一个UnsatisfiedLinkError

要能够调用您的包装器方法,您要做加载该类,因此VM 可以绑定本机方法。要仅通过调用System.load()来实现此目的,请确保VM已具有该类。也就是说,最好使用通常的方法从类本身的静态初始化程序中加载本机库。 loadLibrary还将找到静态链接的库。因此,如果您将JNI函数与其余代码分开,然后将它们放入自己的库中,则可以静态链接它们并使用带有简单名称的loadLibrary