文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2024-04-02 19:55

关注

背景

近期要将算法部署到一个机群的虚拟主机(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内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯