使用JNI链接静态库

24

Java 8及其以上版本允许使用静态链接库(static linked libraries)来使用JNI,不再要求本地代码必须在共享库中。但是,在Java 8之前的版本需要将本地代码打包成共享库。我查找了一些示例,但没有找到相关的内容。

如何将JNI库静态链接到Java应用程序中呢?


你在哪里看到的?请提供一些证据 :-) - Gyro Gearless
在维基百科的Java版本历史页面上看到了这个,他们的来源:http://openjdk.java.net/jeps/178 - chmod
3个回答

18
Java SE 8规范已经更新以支持静态链接,并且在JDK中实现了静态链接。在System.loadLibrary的规范中简短提到了这一点。它所涉及的JNI规范部分在此处此处
静态链接和动态链接方法的本机方法签名和数据类型相同。但是,您可能需要修改JDK makefile才能使库静态链接。
一个重要的区别是静态库的初始化方式。通过调用JNI_OnLoad函数来初始化动态库,并通过调用JNI_OnUnload来反初始化。每个动态库都可以拥有自己版本的这些函数。如果有多个静态链接库,那么它们都不能拥有这些相同名称的函数。对于名为libname的静态库,加载/卸载函数是JNI_OnLoad_libnameJNI_OnUnload_libnameJNI_OnLoad_libname函数必须返回JNI_VERSION_1_8或更高版本的值。如果没有,JVM将忽略此静态库。
基本上,如果您调用System.loadLibrary("foo"),系统会在运行的可执行映像中查找函数JNI_OnLoad_foo,如果找到它,则假定该库被静态链接,并且在运行映像中搜索其本机方法。如果未找到JNI_OnLoad_foo,则会发生通常的动态库搜索和加载,并从找到的动态库链接本机方法。

1
@chmod 写本地方法的机制应该与动态本地方法相同。与静态本地方法不同的是,您需要编译和链接本地代码以生成静态库,然后执行第二步将该静态库链接到 JVM 本身。 - Stuart Marks
好的,你知道我怎样修改我的JDK才能让它与我的库一起工作吗? - chmod
2
@chmod 我认为您无法修改预构建的JDK,因为(我相信)无法将附加的静态库链接到可执行文件中。如果您正在从源代码构建,则可能需要使用其他参数或变量设置来修改makefile以向链接器命令提供静态库。例如,请参见OpenJDK中的vm.make,接近第320行及其后面的内容。 - Stuart Marks
@basicthinker 我对这个是如何组合在一起的有点远,但静态链接支持主要针对嵌入式环境,我相信包括libjvm在内的所有内容都是静态链接的。这个想法是JVM和所有静态库提前静态链接,因此混合使用PIC和非PIC库不是问题。我不确定如何让OpenJDK进行静态链接。就像我上面说的,你可能需要修改makefiles。 - Stuart Marks
3
不要忘记让你的 JNI_OnLoad_X 函数至少返回 JNI_VERSION_1_8,否则该库将被静默忽略。 - lesenk
显示剩余2条评论

5
根据您在评论中提到的JEP 178,您无需进行任何不同的操作。 System.loadLibrary 现在将同时加载动态和静态库。
“不需要更改现有Java代码即可使用静态本地库而不是动态本地库。特别是,形式为 System.loadLibrary("foo") 的方法调用应该能够加载“foo”库,无论该库是以静态还是动态形式提供的。”
您可能只需要确保设置了正确的 java.library.path。

那么唯一需要与共享库不同的是编译方式吗?C / C++ 代码没有变化? - chmod
@user3525110 Java或C代码均无更改。只需使用您通常用于从C编译的对象文件创建共享或静态库的编译器选项即可。 - dkatzel
这真的有效吗?我得到了一个静态库(.a文件),但即使我正确设置了java.library.path,该库也无法被找到:“Exception in thread "main" java.lang.UnsatisfiedLinkError: no <library> in java.library.path”。我正在使用Java 8和Debian。 - little_planet
4
不可以简单地将静态库复制到路径中。新方法仅适用于将JVM嵌入其可执行文件的人。 - Alex Cohn

2

Java 8的增强功能https://openjdk.java.net/jeps/178是为JVM而设计的。

给定两个文件:

  • Main.java
  • Main.c
创建libnative.so:
javac Main.java
javah Main
gcc -c Main.c
gcc -c Main.c -I /home/dx/.sdkman/candidates/java/current/include/linux -I /home/dx/.sdkman/candidates/java/current/include
gcc -shared -o libnative.so Main.o

创建 libnative.a:
ar -cvq libnative.a Main.o

对于每个通过 libnative.a、libnative.so 进行测试运行的文件:
java -Djava.library.path=.  Main

结果:

  • libnative.so 执行成功
  • libnative.a 执行失败

这证明 178 是针对 JVM 的。

参考文献:


那么在Linux上不能使用静态库吗?这在Windows上是可以的。 - Tobi Akinyemi
-Djava.library.path选项是专门用于指定在运行时使用JNI加载的动态/共享库的路径。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接