System.loadLibrary不起作用。 链中的第二个lib的UnsatisfiedLinkError
我有通过JNI使用cpp共享库libclient.so的java程序Client.class。 libclient.so构建为共享并使用cpp共享库libhttp.so。
libclient.so和libhttp.so放在文件夹/home/client/lib64
Client.class放在/home/client/bin
客户端可以加载库
- System.load和环境变量LD_LIBRARY_PATH
- System.loadLibrary和-Djava.library.path
第一种方式很好。
export LD_LIBRARY_PATH = /home/client/lib64
java -classpath ./bin客户端
secon方式失败了。
java -classpath ./bin -Djava.library.path=./../lib64 Client
java.lang.UnsatisfiedLinkError: /home/client/lib64/libclient.so: libhttp.so: cannot open shared object file: No such file or directory
当我将libhttp.so放入/ usr / lib64时,第二种方式可以正常工作。
如果我使用System.loadLibrary,为什么libclient.so在/ usr / lib64中寻找libhttp.so? 如何在不将libhttp.so复制到/ usr / lib64的情况下修复它?
我的加载代码:
//Try load from -Djava.library.path boolean found = false; String lib = "client"; try { System.loadLibrary(lib); found = true; } catch (UnsatisfiedLinkError e) { e.printStackTrace(); } //Try load from LD_LIBRARY_PATH if (!found) { lib = "libclient.so"; String ld_lib_path = System.getenv("LD_LIBRARY_PATH"); String[] paths = ld_lib_path.split(":"); for(int i=0; i<paths.length; i++) { String p = paths[i]; File x = new File(p, lib); if (x.exists()) { System.load(x.getAbsolutePath()); found = true; break; } } }
附加信息。
如果我用ldd测试libclient.so然后我看到:libhttp.so => not found如果我设置export LD_LIBRARY_PATH = / home / client / lib64然后我看到:libhttp.so => /home/client/lib64/libhttp.so
这样做的原因是libclient.so是从您的JVM加载的,它在java.library.path
中查找。 但是,当libclient.so尝试加载libhttp.so时,它对Java一无所知,只使用常规Linux方式加载共享库(动态链接器ld.so
),它查找LD_LIBRARY_PATH
和一些常见目录,如/usr/lib64
。
我可能会使用从Java应用程序的启动脚本中设置的LD_LIBRARY_PATH
。 如果您不想使用启动脚本,理论上可以在进程本身内设置LD_LIBRARY_PATH
。 但是,Java不允许这样做(只有System.getenv()
,而不是System.setenv()
),因此您需要编写一个从Java调用的小型C库并调用putenv()
设置LD_LIBRARY_PATH
。
如果您自己构建libclient.so
,则可以使用libclient.so
链接器标志指定动态链接器应查找其他所需库的路径。 如果在此处指定相对路径,请小心,它将被解释为相对于正在运行的应用程序的当前工作目录,而不是相对于libclient.so
的位置。 要实现这一点,您需要使用$ORIGIN
作为-rpath
参数,并注意您的shell不会扩展它。
因此,如果您想在同一目录中安装libclient.so
和libhttp.so
,则需要使用
-rpath '$ORIGIN'
作为构建libclient.so
时链接器的参数。 如果不直接调用链接器但让编译器调用它,则需要将以下内容添加到编译器的命令行:
-Wl,-rpath,'$ORIGIN'
有关这方面的更多信息,请参见ld.so
的手册页 。
我对这个问题没有好的答案。
但我找到了几个好方法。
- 将libhttp.so放入库的共享位置,例如/ usr / lib64。
- 将libhttp.so的路径放入LD_LIBRARY_PATH。
- 使用libhttp.so构建libclient.so。
- 在构建libclient.so时使用-rpath。
为了正确查找不同操作系统的库(来自java.library.path ),必须具有不同的名称:
- Linux: libhttp.so
- Windows: http.dll
你可以从Java调用:
System.loadLibrary( "http" );