如果要加载类的多个版本,如果它们实现共享接口并且位于单独的JAR using a separate class loader for each version中,则可以这样做。
如果您有一个调用本机代码的JAR,您可以在其JAR by extracting the shared library to a temporary file and then using System.load
to load the library from the temporary file中存储本机代码的共享库(DLL)。
但如果你同时做到这两点,它会起作用吗?如果两个版本的JAR调用本机代码,并且两者都包含不同版本的共享库,会发生什么?
让我们假设两个JAR使用不同的临时文件来存储共享库的副本。但是共享库的两个版本具有本机代码,这些代码调用具有相同声明的本机(C)函数(但这些函数的实现是不同的)。 JVM /类加载器/ System.load
是否会从Java代码委托给正确的本机代码?或者JVM会抱怨名称冲突吗?
如果该方案 失败,如何我使用使用本机代码的类的多个版本?
答案 0 :(得分:3)
检查Open JDK 7实现,似乎是的,加载使用本机代码的多个版本的Java类工作:
关键信息是System.load
如何表现?该方法的实现将取决于系统,但各种实现的语义应该相同。
System.load
委托包私有方法Runtime.load0
。Runtime.load0
委托给包私有静态方法ClassLoader.loadLibrary
。ClassLoader.loadLibrary
委托私有静态方法ClassLoader.loadLibrary0
。ClassLoader.loadLibrary0
创建包私有内部类ClassLoader.NativeLibrary
的对象,并委托其load
方法。ClassLoader.NativeLibrary.load
是一种本机方法,它委托给函数JVM_LoadLibrary
。JVM_LoadLibrary
代表os::dll_load
。os::dll_load
取决于系统。os::dll_load
委托dlopen
系统调用,提供RTLD_LAZY
选项。dlopen
具有RTLD_LOCAL
行为,因此共享库加载了RTLD_LOCAL
语义。RTLD_LOCAL
语义是加载的库中的符号不可用于后续加载的库的(自动)符号解析。也就是说,符号不会进入全局命名空间,并且不同的库可以定义相同的符号而不会产生冲突。共享库could even have identical content without problems。extern
函数名称)并不重要:JRE和JVM一起避免名称冲突。 / LI>
醇>
这可确保共享库的多个版本不会生成名称冲突。但OpenJDK如何确保将正确的 JNI代码用于本机方法调用?
SharedRuntime::generate_native_wrapper
中。但是,最终需要知道要调用的JNI函数的地址。methodHandle
C ++对象,根据需要从methodHandle::critical_native_function()
或methodHandle::native_function()
获取JNI函数的地址。methodHandle
致电methodHandle::set_native_function
,NativeLookup::lookup
会记录JNI功能的地址。NativeLookup::lookup
代表,间接地,NativeLookup::lookup_style
NativeLookup::lookup_style
委托Java包 - 私有静态方法ClassLoader.findNative
。ClassLoader.findNative
按照ClassLoader.nativeLibraries
设置的ClassLoader.NativeLibrary
个对象的列表(ClassLoader.loadLibrary0
)按照加载库的顺序进行迭代。对于每个库,它委托NativeLibrary.find
尝试查找感兴趣的本机方法。虽然此对象列表不公开,但JNI specification要求JVM“维护每个类加载器的已加载本机库列表”,因此所有实现都必须具有与此列表类似的内容。NativeLibrary.find
是一种原生方法。它只是委托给JVM_FindLibraryEntry
。JVM_FindLibraryEntry
委托系统相关方法os::dll_lookup
。os::dll_lookup
的Linux实现委托给dlsym
系统调用,以查找共享库中函数的地址。答案 1 :(得分:-1)
如果您尝试在不同的类加载器中加载相同的库,您将获得UnsatisfiedLinkError
消息" Native Library:...已经加载到另一个类加载器& #34 ;.当类加载器被垃圾收集(https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#compiling_loading_and_linking_native_methods)时,这可能与调用库的卸载方法的VM有关。
但是,如果您 - 正如您所说 - "使用不同的临时文件来存储共享库的副本"无论文件是什么,这两个都是有效的不同的库。内容(可能是二进制相同,无关紧要)。所以没有问题。