JNI 运行时链接

Java™ 本机接口 (JNI) 支持运行时链接到动态和静态本机库。

请参阅以下各部分,了解有关特定于平台的信息:

特定于 AIX 系统的信息

对于运行时链接,可以使用 -brtl 装入程序选项来链接应用程序。 如果运行时链接导致符号冲突,那么应用程序必须通过重命名应用程序端的符号或关闭运行时链接来解决该冲突。

AIX 中的动态链接

要动态链接本机库,应将本机方法 (Java 调用的 C 或 C++ 函数) 编译为 AIX 共享对象 (动态装入的库)。 例如,如果您的本机方法存储在文件 nm.c 中,那么可以通过以下命令创建共享对象:
cc_r -qmkshrobj [-qarch=ppc | -q64] -Ijava_install_dir/include 
-o libnm.a nm.c
-qmkshrobj 选项可禁用运行时链接。 有关共享对象文件、运行时链接以及使用 ccld 命令行选项的更多信息,请参阅:

在运行使用本机方法的 Java 程序之前,请确保 LIBPATH 包含用于存放本机方法的共享对象的目录列表。 有关构建 AIX 共享对象的更多信息,请参阅 C 和 C++ Application Development on AIX 。 转至 https://www.ibm.com/redbooks 并搜索 "SG245674"。

如果在 JNI 本机代码程序上设置 setuidsetgid 属性,那么该设置会更改有效的 LIBPATH 环境变量。 这种更改可能会引起程序发生意外或不正确的行为。 有关此用法的更多信息,请参阅 在 AIX 上开发和移植 C 和 C++ 应用程序 ,网址为 http://www.redbooks.ibm.com/abstracts/sg245674.html,第 2.3.3。

构建使用 JNI 调用 API 创建 Java 虚拟机并调用 Java 代码的 C 或 C++ 程序时,请使用 -L 选项执行以下任务:

  • /usr/lib/lib 添加到要搜索共享对象的目录列表中。 所有程序都需要这些目录中存储的共享对象。
  • 将 Java java_install_dir/jre/libjava_install_dir/jre/lib/j9vm 目录添加到搜索共享对象的目录列表中。 这些目录包含 Java 共享库。 您也可能希望与 libjvm.so 链接(通过使用 -ljvm 选项)。

例如,以下代码将构建一个使用 JNI 调用 API 的 C 程序 (invAPITest.c):

cc_r [-qarch=pwr4 | -q64] -Ijava_install_dir/include 
	-o invAPITest 
	-L/usr/lib 
	-L/lib 
	-Ljava_install_dir/jre/lib/j9vm 
	-Ljava_install_dir/jre/lib 
	-ljvm invAPITest.c

运行使用 JNI 调用 API 来运行 Java 类的 C 或 C++ 程序时,请确保正确设置了类路径以允许 JVM 查找类文件。 如果修改 Java 引导类路径,请包含运行应用程序所需的 SDK 文件。

要确定正在使用 JNI 调用 API 的 C 或 C++ 程序是否是使用 -bM:UR 选项构建的,请使用以下命令:

dump [-X64] -ov <program name> 

将生成类似以下内容的输出:

>dump -X64 -ov <program name>
                        ***Object Module Header***
# Sections      Symbol Ptr      # Symbols       Opt Hdr Len     Flags
         4      0x0001a728           1305               120     0x1002
Flags=( EXEC DYNLOAD DEP_SYSTEM )
Timestamp = "14 Oct 03:26:43 2005"
Magic = 0x1f7  (64-bit XCOFF)

                        ***Optional Header***
Tsize        Dsize       Bsize       Tstart      Dstart
0x000127f8  0x00001b80  0x00000470  0x1000001f8  0x1100009f0

SNloader     SNentry     SNtext      SNtoc       SNdata
0x0004      0x0002      0x0001      0x0002      0x0002    

TXTalign     DATAalign   TOC         vstamp      entry
0x0005      0x0003      0x110002158  0x0001      0x110002040

maxSTACK     maxDATA     SNbss       magic       modtype
0x00000000  0x00000000  0x0003      0x010b        UR
如果modtype不为UR,您可以使用 LDR_CNTRL 环境变量使程序的行为与使用 -bM:UR 绑定程序选项编译的程序一样。 例如:
export LDR_CNTRL=USERREGS

如果需要使用 LDR_CNTRL 指定多个选项,请使用 @ 符号来分隔这些选项。

JVM 创建的 Java 线程使用 AIX上支持的 POSIX pthreads 模型。 目前,此方法基于内核线程的一对一映射。 在开发 JNI 程序时,如果要在您自己的程序中创建多线程,必须使用一对一线程模型和系统争用作用域来进行操作。 您可以使用以下环境设置控制此行为:
export AIXTHREAD_SCOPE=S

另一个选项是在创建线程时使用 AIX pthread_attr_setscope 函数将线程的作用域属性预设为 PTHREAD_SCOPE_SYSTEM

您可以按如下所述存储本机方法:
共享对象
共享对象是在 XCOFF 标题中具有 SRE(共享重复性)位设置的单个对象文件。 SRE 位可以识别该文件动态链接到的链接程序。 这些文件通常具有格式为 <filename> .o 的名称,但它们也可以命名为 lib<name> .a,以允许链接程序使用 -lname 选项来搜索这些文件;但这些文件不是归档库文件。
共享库
共享库是“ar”格式的归档库,其中有一个或多个归档成员是共享对象。 请注意,该库也可以包含以静态方式链接的非共享对象文件。 共享库具有 lib<name> .a 格式的名称。 此格式使链接程序能够使用 -lname 选项来搜索库。

有关更多信息,请参阅 AIX 文档

例如,程序可以通过使用 dlopen() 系列子例程来动态链接至共享库和共享对象。 JVM 在装入本机库(例如 System.load()System.loadLibrary()Runtime.getRuntime().load()Runtime.getRuntime().loadLibrary())时以这种方式进行链接。

有关 dlopen 的信息,请参阅 dlopen 子例程

有关 AIX 装入和链接机制的信息,请参阅 AIX 链接和装入机制

要装入 AIX 共享库,请调用:
System.loadLibrary("<library>(<member>)")
其中,<library> 是共享库归档的名称,而 <member> 则是归档成员的名称。 例如:
System.loadLibrary("libShared.a(libSample.o)")
注: 要确保本机库的动态链接成功工作,您可以 (可选) 在库中实现生命周期函数 JNI_Onload()JNI_OnUnload() 。 如果实现了 JNI_Onload(),那么本机库必须将其导出,否则该函数对于运行时不可见,并且 JVM 会认为该库只需要 JNI 版本 JNI_VERSION_1.1。 如果实现了 JNI_OnUnload(),也必须将其导出。 如果实现并导出了 JNI_Onload(),那么将返回最新的 JNI 版本;例如,JNI_VERSION_1.8。

AIX 中的静态链接

除了动态链接,您还可以静态链接到 JNI 库。 静态库可以合并到使用调用 API 启动 JVM 进程的定制启动程序的映像中。

请考虑以下两个静态库:testlibA 和 testlibB。 如果您尝试使用 System.loadLibrary("testlibA") 之类的命令在Java程序中加载 testlibA 库,JVM首先会在启动的可执行程序映像中查找名为 JNI_OnLoad_testlibA( 的例程。如果找到该例程,JVM将使用该静态绑定库提供JNI定义。 如果未找到例程,JVM 将回退以在 -Djava.library.path(或 LIBPATH)中指定的路径中查找来动态装入 testlibA 库(例如 libtestlibA.so)。

在 AIX上,除了通过指定 -brtl来启用运行时链接外,还必须使用指定的选项 -bexpall 进行构建。 该过程可确保启动程序导出符号(例如 JNI_OnLoad_testlibA()),还允许可执行程序装入的共享库(例如 JVM 的共享库)查找符号。 为使 JVM 能够静态链接,它必须能够在进行任何动态链接尝试之前在可执行程序中重新查找。

您还可以使用以下命令构建具有静态库的可执行程序:
$ cc_r -qpic -brtl -bexpall -brtl testlibA.o testlibB.o -o <launcher>
其中 <launcher> 是包含 testlibA 和 testlibB 库的映像的可执行程序的名称,并且是通过调用 API 启动 JVM 的 Java 启动程序。

库初始化例程 JNI_OnLoad_L, 对于库L,必须返回JNI_VERSION_1_8(0x00010008).

特定于 Linux 系统的信息

如果运行时链接导致符号冲突,那么应用程序必须通过重命名应用程序端的符号或关闭运行时链接来解决该冲突。

Linux 中的动态链接

构建使用 JNI 调用 API 创建 Java 虚拟机并调用 Java 代码的 C 或 C++ 程序时,请使用 -L 选项执行以下任务:

  • /usr/lib/lib 添加到要搜索共享对象的目录列表中。 所有程序都需要这些目录中存储的共享对象。
  • 将 Java java_install_dir/jre/libjava_install_dir/jre/lib/j9vm 目录添加到搜索共享对象的目录列表中。 这些目录包含 Java 共享库。 您也可能希望与 libjvm.so 链接(通过使用 -ljvm 选项)。

例如,以下代码将构建一个使用 JNI 调用 API 的 C 程序 (invAPITest.c):

cc [-m32|-m64] -Ijava_install_dir/include 
	-o invAPITest 
	-L/usr/lib 
	-L/lib 
	-Ljava_install_dir/jre/lib/j9vm 
	-Ljava_install_dir/jre/lib 
	-ljvm invAPITest.c

运行使用 JNI 调用 API 来运行 Java 类的 C 或 C++ 程序时,请确保正确设置了类路径以允许 JVM 查找类文件。 如果修改 Java 引导类路径,请包含运行应用程序所需的 SDK 文件。

要确保 JNI 库导出 Java 应用程序在运行时必须解析的函数,可以使用 nm 工具来检查该库。 例如,名为 libjnitest.so 且包含 JNI 例程 fooImplbarImpl 的 JNI 库必须导出以下符号:

$ nm libjnitest.so
000537d0 T Java_mypackage_SampleClass_fooImpl
0004f020 T Java_mypackage_SampleClass_barImpl

同样,命令 objdump -T 列出共享库中导出的符号:

000537d0  g    DF .text  00000040  Base        0x60 
Java_mypackage_SampleClass_fooImpl
0004f020  g    DF .text  00000254  Base        0x60 
Java_mypackage_SampleClass_barImpl
您可以按如下所述存储本机方法:
共享库
Linux 上的共享库 (或共享对象) 是可以在构建时 (链接时链接) 或运行时 (运行时链接) 链接的动态链接库。 其中,对共享库的链接时链接通过使用链接程序编辑器工具 ld 来完成,而运行时链接使用 dlopen(3) 系列函数。

例如,程序可以通过使用 dlopen() 系列子例程来动态链接至共享库和共享对象。 JVM 在装入本机库(例如 System.load()System.loadLibrary()Runtime.getRuntime().load()Runtime.getRuntime().loadLibrary())时以这种方式进行链接。

有关 dlopen(3)的信息,请参阅 Linux man 文档并搜索 dlopen

注: 要确保本机库的动态链接成功工作,您可以 (可选) 在库中实现生命周期函数 JNI_Onload()JNI_OnUnload() 。 如果实现了 JNI_Onload(),那么本机库必须将其导出,否则该函数对于运行时不可见,并且 JVM 会认为该库只需要 JNI 版本 JNI_VERSION_1.1。 如果实现了 JNI_OnUnload(),也必须将其导出。 如果实现并导出了 JNI_Onload(),那么将返回最新的 JNI 版本;例如,JNI_VERSION_1.8。

Linux 中的静态链接

除了动态链接,您还可以静态链接到 JNI 库。 静态库可以合并到使用调用 API 启动 JVM 进程的定制启动程序的映像中。

请考虑以下两个静态库:testlibA 和 testlibB。 如果您尝试使用 System.loadLibrary("testlibA") 之类的命令在Java程序中加载 testlibA 库,JVM首先会在启动的可执行程序映像中查找名为 JNI_OnLoad_testlibA( 的例程。如果找到该例程,JVM将使用该静态绑定库提供JNI定义。 如果未找到例程,JVM 将回退以在 -Djava.library.path(或 LD_LIBRARY_PATH)中指定的路径中查找来动态装入 testlibA 库(例如 libtestlibA.so)。

在 Linux 上,您必须使用指定的选项 -rdynamic 进行构建,以确保启动器导出符号(例如 JNI_OnLoad_testlibA( ),并允许可执行程序(例如JVM)加载的共享库查找符号。 为使 JVM 能够静态链接,它必须能够在进行任何动态链接尝试之前在可执行程序中重新查找。 如果不指定 -rdynamic,那么仍会从可执行程序中导出非静态符号,但这些符号对于该程序装入的共享库不可见。 以下示例显示了 -rdynamic 的用法:

$ cc -rdynamic testlibA.o testlibB.o -o <launcher>

库初始化例程 JNI_OnLoad_L, 对于库L,必须返回JNI_VERSION_1_8(0x00010008).

特定于 Windows 系统的信息

如果运行时链接导致符号冲突,那么应用程序必须通过重命名应用程序端的符号或关闭运行时链接来解决该冲突。

Windows 中的动态链接

构建使用 JNI 调用 API 创建 Java 虚拟机并调用 Java 代码的 C 或 C++ 程序时,请使用链接程序选项 /link /LIBPATH 来执行以下任务:

  • 将 Java java_install_dir\jre\bin\java_install_dir\jre\bin\j9vm 目录添加到搜索共享对象的目录列表中。 这些目录包含 Java 共享库。 您也可能希望与 jvm.dll 链接(通过使用 -ljvm 选项)。

在 Windows 上,命令行编译器 cl.exe不需要特殊选项。 通常,使用 Microsoft 编译器,例如 VC + +。有关编译器选项的更多信息,请参阅所使用的编译器的文档。 缺省情况下,VC++ 选取环境变量 %LIB% 中存在的库。 该变量必须始终指向 VC++ SDK 的 \lib 子目录,以作为用于链接库的其中一个搜索路径。

以下是用于构建调用 API 测试的典型命令块:
cl.exe /I java_install_dir\jre\include
	/FeinvAPITest 
	invAPITest.c 
	/link /LIBPATH:java_install_dir\jre\bin\j9vm 
	/LIBPATH:java_install_dir\jre\bin

运行使用 JNI 调用 API 来运行 Java 类的 C 或 C++ 程序时,请确保正确设置了类路径以允许 JVM 查找类文件。 如果修改 Java 引导类路径,请包含运行应用程序所需的 SDK 文件。

要确保 JNI 库导出 Java 应用程序在运行时必须解析的函数,您可以使用 dumpbin.exe 工具 (通常是 VC++ SDK 安装的一部分) 来检查该库。 例如,名为 jnitest.dll 且包含 JNI 例程 fooImplbarImpl 的 JNI 库必须导出以下符号:
C:\>dumpbin.exe /EXPORTS jnitest.dll
Dump of file jnitest.dll

File Type: DLL

      Section contains the following exports for JNITEST.dll

00000000 characteristics
5412A472 time date stamp Fri Sep 12 03:44:50 2014
    0.00 version
       1 ordinal base
       5 number of functions
       5 number of names

ordinal hint RVA      name
...
     1   27 0000CE10 Java_mypackage_SampleClass_fooImpl = Java_mypackage_SampleClass_fooImpl
     2   28 000085A0 Java_mypackage_SampleClass_barImpl = Java_mypackage_SampleClass_barImpl
...

有关 dumpbin.exe 及其选项的更多信息,请参阅 MSDN 文档。

您可以按如下所述存储本机方法:
动态链接库
在 Windows 上, JNI 方法通常存储在称为 "动态链接库" (DLL) 的动态库中。 DLL 包含可以从另一装入模块(例如,动态库或可执行程序)引用的函数和数据。 本机方法存储在 DLL 中,并在构建时通过链接过程或在运行时通过使用 Windows API LoadLibrary()LoadLibraryEx() 函数动态装入方法进行链接。 有关 LoadLibrary() 系列函数的更多信息,请参阅 MSDN 文档。
注: 要确保本机库的动态链接成功工作,您可以 (可选) 在库中实现生命周期函数 JNI_Onload()JNI_OnUnload() 。 如果实现了 JNI_Onload(),那么本机库必须将其导出,否则该函数对于运行时不可见,并且 JVM 会认为该库只需要 JNI 版本 JNI_VERSION_1.1。 如果实现了 JNI_OnUnload(),也必须将其导出。 如果实现并导出了 JNI_Onload(),那么将返回最新的 JNI 版本;例如,JNI_VERSION_1.8。

Windows 中的静态链接

除了动态链接,您还可以静态链接到 JNI 库。 静态库可以合并到使用调用 API 启动 JVM 进程的定制启动程序的映像中。

请考虑以下两个静态库:testlibA 和 testlibB。 如果您尝试使用 System.loadLibrary("testlibA") 之类的命令在Java程序中加载 testlibA 库,JVM首先会在启动的可执行程序映像中查找名为 JNI_OnLoad_testlibA( 的例程。如果找到该例程,JVM将使用该静态绑定库提供JNI定义。 如果未找到例程,JVM 将回退以在 -Djava.library.path(或 PATH)中指定的路径中查找来动态装入 testlibA 库(例如 testlibA.dll)。

在构建启动程序可执行程序时, Windows 不会强制指定任何特殊选项。

库初始化例程 JNI_OnLoad_L, 对于库L,必须返回JNI_VERSION_1_8(0x00010008).

特定于 z/OS 系统的信息

如果运行时链接导致符号冲突,那么应用程序必须通过重命名应用程序端的符号或关闭运行时链接来解决该冲突。

z/OS 中的动态链接

构建使用 JNI 调用 API 创建 Java 虚拟机并调用 Java 代码的 C 或 C++ 程序时,请使用 -L 选项执行以下任务:

  • /usr/lib/lib 添加到要搜索共享对象的目录列表中。 所有程序都需要这些目录中存储的共享对象。
  • 将 Java java_install_dir/jre/libjava_install_dir/jre/lib/j9vm 目录添加到搜索共享对象的目录列表中。 这些目录包含 Java 共享库。 您也可能希望与 libjvm.so 链接(通过使用 -ljvm 选项)。

例如,以下代码通过编译 C 程序 invAPITest.c 来构建名为 invAPITest 的调用 API 启动程序:

cc [-q32|-q64] -Ijava_install_dir/jre/include 
	-o invAPITest 
	-L/usr/lib 
	-L/lib 
	-Ljava_install_dir/jre/lib/j9vm 
	-Ljava_install_dir/jre/lib 
	-ljvm invAPITest.c

-q32-q64 用于指定构建程序时所使用的数据模型。 如果省略这些值,将使用缺省数据模型。

运行使用 JNI 调用 API 来运行 Java 类的 C 或 C++ 程序时,请确保正确设置了类路径以允许 JVM 查找类文件。 如果修改 Java 引导类路径,请包含运行应用程序所需的 SDK 文件。

要确保 JNI 库导出 Java 应用程序在运行时必须解析的函数,您可以使用 nm 工具来检查该库。 例如,名为 libjnitest.so 且包含 JNI 例程 fooImplbarImpl 的 JNI 库必须导出以下符号:

$nm libjnitest.so
     255552 T Java_mypackage_SampleClass_fooImpl
     255528 T Java_mypackage_SampleClass_barImpl

有关更多信息,请参阅 编译器选项缺省值

您可以按如下所述存储本机方法:
动态链接库
在 IBM Z® 系统上, JNI 方法通常存储在称为 "动态链接库" (DLL) 的动态库中。 DLL 包含可以从另一装入模块(例如,动态库或可执行程序)引用的函数和数据。 本机方法存储在 DLL 中,并在构建时通过链接过程或在运行时通过使用 IBM Z API dllload 或符合 POSIX的 API dlopen动态装入方法进行链接。 有关 dllload () 和 dlopen () 函数的更多信息,请参阅 装入 DLL

例如,程序可以通过使用 dlopen()dllload() 系列子例程来动态链接至共享库和共享对象。 JVM 在装入本机库(例如 System.load()System.loadLibrary()Runtime.getRuntime().load()Runtime.getRuntime().loadLibrary())时以这种方式进行链接。

有关这些子例程的信息,请参阅 dlopen ()dllload ()

注: 要确保本机库的动态链接成功工作,您可以 (可选) 在库中实现生命周期函数 JNI_Onload()JNI_OnUnload() 。 如果实现了 JNI_Onload(),那么本机库必须将其导出,否则该函数对于运行时不可见,并且 JVM 会认为该库只需要 JNI 版本 JNI_VERSION_1.1。 如果实现了 JNI_OnUnload(),也必须将其导出。 如果实现并导出了 JNI_Onload(),那么将返回最新的 JNI 版本;例如,JNI_VERSION_1.8。

z/OS 中的静态链接

除了动态链接,您还可以静态链接到 JNI 库。 静态库可以合并到使用调用 API 启动 JVM 进程的定制启动程序的映像中。

请考虑以下两个静态库:testlibA 和 testlibB。 如果您尝试使用 System.loadLibrary("testlibA") 之类的命令在Java程序中加载 testlibA 库,JVM首先会在启动的可执行程序映像中查找名为 JNI_OnLoad_testlibA( 的例程。如果找到该例程,JVM将使用该静态绑定库提供JNI定义。 如果未找到例程,JVM 将回退以在 -Djava.library.path(或 LIBPATH)中指定的路径中查找来动态装入 testlibA 库(例如 libtestlibA.so)。

在 IBM Z 系统上,构建时必须指定以下选项:
  • Wc,DLL,以确保可执行程序构建为 DLL
  • Wc,EXPORTALL,以确保导出可执行程序中的符号,并使其可供该程序装入的共享库查找
这些选项使启动程序可执行程序能够导出符号(例如 JNI_Onload_testlibA()),以便 JVM 在尝试动态链接之前查找它们。
以下示例使用编译和链接行来确保将静态库 testlibA 和 testlibB 链接到构建为 DLL 并导出所有符号的启动程序。
cc -c -Wc,DLL,EXPORTALL launcher.c -o launcher.o
cc testlibA.o testlibB.o launcher.o -o launcher

或者,您也可以除去 EXPORTALL,在这种情况下,程序通过使用 #pragma export 来导出特定符号。 有关更多信息,请参阅 #pragma 导出

注: 在 IBM Z 系统上,如果可执行程序通过使用静态链接来封装 Java 程序必须引用的 JNI 例程,那么除了导出符号外,还必须将可执行程序构建为动态链接库 (DLL)。 如果 IBM Z 可执行程序定义和导出 JNI_OnLoad_testlibA,但其本身未构建为 DLL (即,未指定 -Wc,DLL ) ,那么静态链接可能会失败,因为会阻止运行时从可执行程序解析回符号。

库初始化例程 JNI_OnLoad_L, 对于库L,必须返回JNI_VERSION_1_8(0x00010008).