只需一篇就让你详细了解 Java 中 so 文件的加载原理( 二 )


 
3.依据是否 isAbsolute 决定是否直接加载 library
 
name 是绝对路径的话,直接创建 File 实例,调用 loadLibrary0(),继续加载该文件 。具体见下章节 。检查 loadLibrary0 的结果,true 即表示加载成功,结束;false 即表示加载失败,抛出 UnsatisfiedLinkError
 
Can't load xxx
 
name 非绝对路径并且获取的 ClassLoader 存在的话,通过 findLibrary() ,根据 so 名称获得 lib 绝对路径,并创建指向该路径的 File 实例 libfile 。并确保该文件的路径是绝对路径 。反之,抛出 UnsatisfiedLinkError 。
 
ClassLoader.findLibrary failed to return an absolute path: xxx
 
此后也是调用 loadLibrary0() 继续加载该文件,并检查 loadLibrary0 的结果,处理同上 。
 
4.假使 ClassLoader 不存在,遍历 system 路径字符串数组的元素 。
 
通过 mapLibraryName() 分别将 lib name 映射到平台关联的 lib 完整名称并返回,具体见下章节 。创建当前遍历的 path 下 libfile 实例 。调用 loadLibrary0() 继续加载该文件,并检查结果:
 

  • true 则直接结束
  • false 的话,通过 mapAlternativeName() 获取该 lib 可能存在的替代文件名,比如将后缀替换为 jnilib
    • 如果再度 map 后的 libfile 不为空,调用 loadLibrary0() 再度加载该文件并检查结果,true 则直接结束;反之,进入下一次循环
 
5.至此,如果仍未成功找到 library 文件,则在 ClassLoader 存在的情况下,到 usr 路径字符串数组中查找 。
  • 遍历 usr 路径字符串数组的元素
    • 后续逻辑和上述一致,只是 map 时候的前缀不同,是 usr_paths 的元素
 
6.最终进行默认处理,即抛出 UnsatisfiedLinkError,提示在 java.library.path propery 代表的路径下也未找到 so 文件 。
 
no xx in java.library.path
 
// java/lang/ClassLoader.javastatic void loadLibrary(Class<?> fromClass, String name,boolean isAbsolute) {ClassLoader loader =(fromClass == null) ? null : fromClass.getClassLoader();if (sys_paths == null) {usr_paths = initializePath("java.library.path");sys_paths = initializePath("sun.boot.library.path");}if (isAbsolute) {if (loadLibrary0(fromClass, new File(name))) {return;}throw new UnsatisfiedLinkError("Can't load library: " + name);}if (loader != null) {String libfilename = loader.findLibrary(name);if (libfilename != null) {File libfile = new File(libfilename);if (!libfile.isAbsolute()) {throw new UnsatisfiedLinkError(...);}if (loadLibrary0(fromClass, libfile)) {return;}throw new UnsatisfiedLinkError("Can't load " + libfilename);}}for (int i = 0 ; i < sys_paths.length ; i++) {File libfile = new File(sys_paths[i], System.mapLibraryName(name));if (loadLibrary0(fromClass, libfile)) {return;}libfile = ClassLoaderHelper.mapAlternativeName(libfile);if (libfile != null && loadLibrary0(fromClass, libfile)) {return;}}if (loader != null) {for (int i = 0 ; i < usr_paths.length ; i++) {File libfile = new File(usr_paths[i],System.mapLibraryName(name));if (loadLibrary0(fromClass, libfile)) {return;}libfile = ClassLoaderHelper.mapAlternativeName(libfile);if (libfile != null && loadLibrary0(fromClass, libfile)) {return;}}}// Oops, it failedthrow new UnsatisfiedLinkError("no " + name + " in java.library.path");} 
/ ClassLoader#initializePath() /
 
从 System 中获取对应 property 代表的 path 到数组中 。
 
1.先调用 getProperty() 从 JVM 中取出配置的路径,默认的是 "" 。
 
其中的 checkKey() 将检查 key 名称是否合法,null 的话抛出 NullPointerException:
 
key can't be null
 
如果为"",抛出 IllegalArgumentException:
 
key can't be empty
 
后面通过 getSecurityManager() 获取 SecurityManager 实例,检查是否存在该 property 的访问权限 。
 
2.如果允许引用路径元素并且存在的话,将路径字符串的 char 取出进行拼接、计算得到路径字符串数组 。
 
3.反之通过 indexOf(/) 统计 / 出现的次数,并创建一个 / 次数 + 1 的数组 。
 
4.遍历该路径字符串,通过 substring() 将各 / 的中间 path 内容提取到上述数组中 。
 
5.最后返回得到的 path 数组 。
 
// java/lang/ClassLoader.javaprivate static String[] initializePath(String propname) {String ldpath = System.getProperty(propname, "");String ps = File.pathSeparator;...i = ldpath.indexOf(ps);n = 0;while (i >= 0) {n++;i = ldpath.indexOf(ps, i + 1);}String[] paths = new String[n + 1];n = i = 0;j = ldpath.indexOf(ps);while (j >= 0) {if (j - i > 0) {paths[n++] = ldpath.substring(i, j);} else if (j - i == 0) {paths[n++] = ".";}i = j + 1;j = ldpath.indexOf(ps, i);}paths[n] = ldpath.substring(i, ldlen);return paths;}


推荐阅读