解析java.library.path和LD_LIBRARY_PATH的介绍与区别

 更新时间:2021年05月11日 10:19:29   作者:gaussrieman123  
这篇文章主要介绍了java.library.path和LD_LIBRARY_PATH的介绍与区别,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

背景

近期要将算法部署到一个机群的虚拟主机(Debian 9.1 gcc 6.3.0)上,采用的是Java + JNI + shared library的方式来完成底层算法能力的部署。

其中需要用到各种第三方库,有从源码编译的,也有直接下载的so,包括OpenCV相关、TensorFlow相关、MKL以OpenMP相关的动态库。

遇到一个问题,libmklml_intel.so 这个库只能放在 LD_LIBRARY_PATH中进行加载,而不能通过java.library.path完成加载,所以有必要搞清楚这两个路径究竟有什么区别。

java.library.path

官方文档的定义是:List of paths to search when loading libraries
从定义我们可以发现,首先是一个list,也就是说可以包括多个地址,然后这些地址是用来帮助jvm搜索需要加载的库文件的。

设置java.library.path

最简单的办法就是在启动jvm前通过java -Djava.library.path=path-to-your-libs设置这个全局变量。

作用

那么这个地址具体是如何被使用的呢?
当我们调用System.loadLibrary(libname)时,会调用Runtime.loadLibary,然后调用java/lang/ClassLoader.loadLibrary。在ClassLoader.loadLibrary中,系统属性java.library.path将会被获取,并用来生成需要加载的库的绝对路径,然后将这个绝对路径传给本地方法来调用dlopen/dlsym并最终加载这个库。
如果加载失败,会根据实际情况返回三个异常值:

SecurityException − if a security manager exists and its checkLink method doesn't allow loading of the specified dynamic library
UnsatisfiedLinkError − if the library does not exist
NullPointerException − if libname is null

可以参考OpenJDK的仓库:

static 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(
    "ClassLoader.findLibrary failed to return an absolute path: " + libfilename);
                }
                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;
            }
        }
        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;
                }
            }
        }
        // Oops, it failed
        throw new UnsatisfiedLinkError("no " + name + " in java.library.path");
    }

LD_LIBRARY_PATH

为了搞清楚这个变量的作用,我们先说明一下Unix系统是如何加载动态库的,然后自然就明白为什么要有LD_LIBRARY_PATH以及如何使用了。

动态库如何加载?

在基于GNU glibc的系统上,包括所有的linux系统,启动一个ELF格式的二进制可执行文件会自动调用加载器加载必要的动态链接库,一个最简单的可执行文件一般也会包含一些系统的动态库比如libc.so等。在Linux系统中,这个加载器叫做/lib/ld-linux.so.X,这个X指的是加载器的版本号。加载器然后查找并加载所需的动态库。

加载器在什么路径中搜索和加载动态库呢——/etc/ld.so.conf,这个文件会包括/etc/ld.so.conf.d/*.conf这些文件夹中所有的.conf文件,而具体的动态库搜索路径,就包含在每个.conf文件中,比如/etc/ld.so.conf.d/libc.conf,它是libc的默认的搜索路径/usr/local/lib,这也是为什么我们不需要显示声明使用系统库却能自动完成加载的原因,也是为什么不同的系统编出来的库无法通用的可见原因之一,因为不同系统的/usr/local/lib目录下的动态库并不一致。

如果每次启动都去查找所有的目录,那样显然是比较笨的做法,所以使用/etc/ld.so.cache来缓存路径,并通过ldconfig来更新这个缓存路径,有兴趣的可以自行查看一下这个缓存文件。实际上,这个缓存路径也很长了,基本上包含了系统可能存放动态库的路径。

为什么有LD_LIBRARY_PATH?

上面我们说到可以通过cache和ldconfig来简化搜索和加载动态库的流程,但是还有两个问题没有考虑到,一是还没有将编出来的库放到系统目录中去,二是依赖库数量很少,不需要经过这么复杂的查找。

LD_LIBRARY_PATH就是用来满足这个需要,它也指定一个搜索路径,且ld-linux.so会优先在这个路径下搜索需要的动态库,如果没找到,再去ld.so.conf中指定的目录寻找。

使用

export LD_LIBRARY_PATH=paths-to-libs

需要注意的一点是,多个目录是通过:隔开的

区别

前面分别介绍了java.library.path 和 LD_LIBRARY_PATH,都是为了加载所需的动态库,有什么区别呢?

前者是在java环境中调用,在jvm启动前设置生效;后者也是在启动前,但是是在Unix环境中使用前者是通过修改property来设置路径;后者是直接增加了ld-linux.so的搜索路径对于JNI直接调用的库,最好使用前者,对于有多重依赖关系的库,最好使用LD_LIBRARY_PATH

参考

HowTo: How to configure library path for JNI dependent libraries
https://zauner.nllk.net/post/0013-jni-and-the-java-library-path/
https://docs.oracle.com/javase/8/docs/api/java/lang/System.html#getProperties–
https://www.tutorialspoint.com/java/lang/runtime_loadlibrary.htm
https://stackoverflow.com/questions/27945268/difference-between-using-java-library-path-and-ld-library-path
Linux关于动态库的文档

到此这篇关于解析java.library.path和LD_LIBRARY_PATH的介绍与区别的文章就介绍到这了,更多相关java.library.path和LD_LIBRARY_PATH内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java遍历Map的几种方法分析

    java遍历Map的几种方法分析

    这篇文章主要介绍了java遍历Map的几种方法,结合实例形式分析了使用循环与迭代等方法操作Map遍历的相关技巧,需要的朋友可以参考下
    2016-08-08
  • 使用Intellij IDEA查看Java源码技巧

    使用Intellij IDEA查看Java源码技巧

    这篇文章主要介绍了使用Intellij IDEA查看Java源码技巧,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-04-04
  • Java基于分治法实现的快速排序算法示例

    Java基于分治法实现的快速排序算法示例

    这篇文章主要介绍了Java基于分治法实现的快速排序算法,结合实例形式分析了java基于分治法的快速排序相关实现技巧,代码中备有较为详细的注释说明便于理解,需要的朋友可以参考下
    2017-12-12
  • Java设计模式之外观模式

    Java设计模式之外观模式

    这篇文章介绍了Java设计模式之外观模式,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-09-09
  • SpringBoot实现定时任务动态管理示例

    SpringBoot实现定时任务动态管理示例

    这篇文章主要为大家介绍了SpringBoot实现定时任务动态管理示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-06-06
  • Java之策略模式比较器案例讲解

    Java之策略模式比较器案例讲解

    这篇文章主要介绍了Java之策略模式比较器案例讲解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • java无限递归遍历json对象问题

    java无限递归遍历json对象问题

    这篇文章主要介绍了java无限递归遍历json对象问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-07-07
  • springboot使用ThreadPoolTaskExecutor多线程批量插入百万级数据的实现方法

    springboot使用ThreadPoolTaskExecutor多线程批量插入百万级数据的实现方法

    这篇文章主要介绍了springboot利用ThreadPoolTaskExecutor多线程批量插入百万级数据,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-02-02
  • Java如何用一个统一结构接收成员名称不固定的数据

    Java如何用一个统一结构接收成员名称不固定的数据

    这篇文章主要为大家详细介绍了Java如何用一个统一结构接收成员名称不固定的数据,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2024-11-11
  • Java Fork/Join框架

    Java Fork/Join框架

    Fork/Join框架是Java7中新增的一项特性,也是Java7平台的其中一项主要改进。下面我们就来简单探讨下Java的Fork/Join框架
    2016-09-09

最新评论