3.到正在加载/卸载 library 的 nativeLibraryContext Stack 中检查是否已经处理中了
- 存在并且 ClassLoader 来源匹配,则结束加载
- 存在但 ClassLoader 来源不同,则抛出 UnsatisfiedLinkError:
- Native Library xxx is being loaded in another classloader
- 反之,继续加载的任务
4.依据 ClassLoader、library 名称、是否内置等信息,创建 NativeLibrary 实例并入 nativeLibraryContext 栈 。
5.此后,交由 NativeLibrary load,细节亦见如下章节,并在 load 后出栈 。
6.最后根据 load 的结果决定是否将加载记录到对应的 Vector 当中 。
// java/lang/ClassLoader.javaprivate static boolean loadLibrary0(Class<?> fromClass, final File file) {// 获取是否是内置动态链接库String name = NativeLibrary.findBuiltinLib(file.getName());boolean isBuiltin = (name != null);if (!isBuiltin) {// 不是内置的话,检查文件是否存在boolean exists = AccessController.doPrivileged(new PrivilegedAction<Object>() {public Object run() {return file.exists() ? Boolean.TRUE : null;}})!= null;if (!exists) {return false;}try {name = file.getCanonicalPath();} catch (IOException e) {return false;}}ClassLoader loader =(fromClass == null) ? null : fromClass.getClassLoader();Vector<NativeLibrary> libs =loader != null ? loader.nativeLibraries : systemNativeLibraries;synchronized (libs) {int size = libs.size();// 检查是否已经加载过for (int i = 0; i < size; i++) {NativeLibrary lib = libs.elementAt(i);if (name.equals(lib.name)) {return true;}}synchronized (loadedLibraryNames) {// 再次检查所有 library 加载历史中是否存在if (loadedLibraryNames.contains(name)) {throw new UnsatisfiedLinkError(...);}int n = nativeLibraryContext.size();// 检查是否已经在加载中了for (int i = 0; i < n; i++) {NativeLibrary lib = nativeLibraryContext.elementAt(i);if (name.equals(lib.name)) {if (loader == lib.fromClass.getClassLoader()) {return true;} else {throw new UnsatisfiedLinkError(...);}}}// 创建 NativeLibrary 实例继续加载NativeLibrary lib = new NativeLibrary(fromClass, name, isBuiltin);// 并在加载前后压栈和出栈nativeLibraryContext.push(lib);try {lib.load(name, isBuiltin);} finally {nativeLibraryContext.pop();}// 加载成功的将该 library 名称缓存到 vector 中if (lib.loaded) {loadedLibraryNames.addElement(name);libs.addElement(lib);return true;}return false;}}} findBuiltinLib()
- 首先一如既往地先检查 library name 是否为空,为空则抛出 Error
- NULL filename for native library
- 将 string 类型的名称转为 char 指针,失败的话抛出 OutOfMemoryError
- 检查名称长度是否短于最起码的 lib.so 几位,失败的话返回 NULL 结束
- 创建 library 名称指针 libName 并分配内存
- 从 char 指针提取 libxxx.so 中 xxx.so 部分到 libName 中
- 将 libName 中 .so 的 . 位置替换成
- 调用 findJniFunction() 依据 handle 指针,library 名称检查该 library 的 JNI_OnLoad() 是否存在
- 存在则释放 libName 内存并返回该函数地址
- 反之,释放内存并返回 NULL 结束
// native/java/lang/ClassLoader.cJava_java_lang_ClassLoader_00024NativeLibrary_findBuiltinLib(JNIEnv *env, jclass cls, jstring name){const char *cname;char *libName;...// 检查名称是否为空if (name == NULL) {JNU_ThrowInternalError(env, "NULL filename for native library");return NULL;}procHandle = getProcessHandle();cname = JNU_GetStringPlatformChars(env, name, 0);// 检查 char 名称指针是否为空if (cname == NULL) {JNU_ThrowOutOfMemoryError(env, NULL);return NULL;}// 检查名称长度len = strlen(cname);if (len <= (prefixLen+suffixLen)) {JNU_ReleaseStringPlatformChars(env, name, cname);return NULL;}// 提取 library 名称(取出前后缀)libName = malloc(len + 1); //+1 for null if prefix+suffix == 0if (libName == NULL) {JNU_ReleaseStringPlatformChars(env, name, cname);JNU_ThrowOutOfMemoryError(env, NULL);return NULL;}if (len > prefixLen) {strcpy(libName, cname+prefixLen);}JNU_ReleaseStringPlatformChars(env, name, cname);libName[strlen(libName)-suffixLen] = '';// 检查 JNI_OnLoad() 释放存在ret = findJniFunction(env, procHandle, libName, JNI_TRUE);if (ret != NULL) {lib = JNU_NewStringPlatform(env, libName);free(libName);return lib;}free(libName);return NULL;} findJniFunction()
findJniFunction() 用于到 library 指针、已加载/卸载的 JNI 数组中查找该 library 名称所对应的 JNI_ONLOAD、JNI_ONUNLOAD 的函数地址 。
推荐阅读
- 一篇讲明白DevOps时代下的持续架构实践
- HTTP缓存看这一篇就够了
- |紫砂壶不挂水就不是好壶吗?
- 天津市|职场人的仕途,无非就是这三个阶段
- 2月逛花市,就选四种漂亮花,浇点水就能活,花开一整年
- 5种花一晒就蔫,放暗处越长越绿,养不好都是弄反了
- 市场营销专业就业方向怎么样,最新市场营销专业当前的就业形势分析
- |老婆打疫苗非要穿露肩连衣裙,戴着口罩就不怕丢脸了吗?
- 出口营销策划方案怎么做,未来出口营销就业前景
- 贺兰山|自己一知半解,却嘲讽壮壮没文化。那就说说《满江红》的贺兰山吧
