JEP 178:静态链接的 JNI 库
概括
增强 JNI 规范以支持静态链接的本机库。
目标
-
修改 Java SE 规范和 JDK,使开发人员能够将 Java 运行时、本机应用程序代码和 Java 应用程序代码一起打包到不需要使用共享本机库的单个二进制可执行文件中。
-
无需更改现有 Java 代码即可使用静态本机库(而不是动态本机库)。特别是,表单的方法调用
System.loadLibrary("foo")
应该能够加载"foo"
库,无论该库是以静态还是动态形式提供的。 -
允许 Java 应用程序使用静态和动态本机库的组合,尽管静态库在尝试使用它们之前必须位于内存中。
-
允许 JVMTI Java 代理选择性地与 Java 运行时静态链接。
非目标
为转换为静态形式的现有动态本机库保留完整的本机 C/C++ 源兼容性并不是目标。JNI_OnLoad
和函数的现有用法JNI_OnUnLoad
需要进行修改,以允许多个静态库共存。
动机
静态 JNI 库在两种主要场景中很有用:
-
嵌入 JRE 的本机应用程序可能希望使用静态链接的 JNI 代码而不是动态链接的库。
-
在限制或不支持共享库的环境中运行的 Java 应用程序需要将 JRE 及其所有本机 API 库代码链接到单个可执行文件中。
另一个好处是,使用静态链接的 JNI 库,目标文件链接器可以优化整个可执行文件,从而有可能减小其大小。
描述
添加对静态 JNI 库的支持需要解决两个主要问题:
-
当前启动动态库加载过程的 Java API 需要增强以支持内置静态库。使用静态 JNI 库的 Java 应用程序需要一种方法来通知 VM 该库代码已包含在应用程序映像中。在这种情况下,
System.loadLibrary
对静态库的请求应该跳过通常的特定于平台的动态加载过程。当前的 JNI 规范暗示了这种类型的支持,尽管 Hotspot VM 并未实现该行为。
-
由于应用程序中只能存在单个函数名称,因此需要增强
JNI_OnLoad
和函数接口以支持库特定名称。JNI_OnUnload
这可以通过将库名称附加到这些众所周知的名称来实现。例如libnet.so
可以使用JNI_OnLoad_net
,JNI_OnUnload_net
。
此功能需要更改 Java SE 库加载 API 和 JNI 规范。以下是这两个领域的规范更新的初步草案。
Java API 变更
的规范java.lang.System.load
和java.lang.Runtime.load
方法将修改为:
加载由文件名参数指定的本机库。文件名参数必须是绝对路径名。
如果文件名参数在去除任何特定于平台的库前缀、路径和文件扩展名后指示名称为 L 的库,并且
statically linked
VM 中有一个名为 L 的本机库,则调用该库导出的 JNI_OnLoad_L 函数而不是尝试加载动态库。与参数匹配的文件名不必存在于文件系统中。有关更多详细信息,请参阅 JNI 规范。否则,文件名参数将以依赖于实现的方式映射到本机库映像。
这些方法何时抛出异常的规范UnsatisfiedLinkError
将修改为:
UnsatisfiedLinkError
- 如果文件名不是绝对路径名,则本机库不在statically linked
VM 中,或者主机系统无法将库映射到本机库映像。
的规范java.lang.System.loadLibrary
和java.lang.Runtime.loadLibrary
方法将修改为:
加载参数指定的本机库
libname
。不得libname
包含任何特定于平台的前缀、文件扩展名或路径。如果调用的本机库
libname
与statically linked
VM 一起,则JNI_OnLoad_libname
调用该库导出的函数。有关更多详细信息,请参阅 JNI 规范。否则,
libname
将从系统库位置加载并以依赖于实现的方式映射到本机库映像。
这些方法何时抛出异常的规范UnsatisfiedLinkError
将修改为:
UnsatisfiedLinkError
- 如果 libname 参数包含文件路径,则本机库不在statically linked
VM 中,或者主机系统无法将库映射到本机库映像。
JNI 规范变更
-
本机库可能
statically linked
与 VM 一起。库和 VM 映像的组合方式取决于实现。 -
一个
System.loadLibrary
或等效的 API 调用必须成功,该库才会被视为已加载。 -
statically linked
当且仅当库导出名为 JNI_OnLoad_L 的函数时,才定义其映像已与 VM 组合的库 L。 -
如果
statically linked
库 L 导出一个名为 的函数JNI_OnLoad_L
和一个名为 的函数JNI_OnLoad
,则该JNI_OnLoad
函数将被忽略。 -
如果库 L 是
statically linked
在第一次调用System.loadLibrary("L")
或等效函数时,则将使用为该JNI_OnLoad_L
函数指定的相同参数和预期返回值来调用该JNI_OnLoad
函数。 -
如果库 L 是
statically linked
,则将禁止动态链接同名库。 -
当包含本机库L的类加载器被垃圾收集时,如果导出了库的函数,则
statically linked
VM将调用该函数。JNI_OnUnload_L
-
如果
statically linked
库 L 导出一个名为 的函数JNI_OnUnLoad_L
和一个名为 的函数JNI_OnUnLoad
,则该JNI_OnUnLoad
函数将被忽略。
JNI 版本规范将增加到JNI_VERSION_1_8
.仅此版本或更高版本支持静态链接库。
JVMTI -agentlib 命令行选项规范更改
-agentlib 命令行规范描述将在 JDK 8 中修改为:
如果
library
参数指示名称为 L 的库,并且statically linked
VM 中有一个名为 L 的本机库,则Agent_OnLoad_L
代理必须导出一个函数。与参数匹配的库不必存在于文件系统中。该Agent_OnLoad_L
函数将由 VM 调用,如 JVMTI 规范中所述。调用时将options
被传递给函数。Agent_OnLoad_L
否则,-agentlib: 后面的名称是要加载的库的名称。库的查找(包括其全名和位置)以特定于平台的方式进行。通常,
agent-lib-name
扩展为操作系统特定的文件名。将options
在启动时传递给代理。例如,如果指定选项 -agentlib:foo=opt1,opt2,VM 将尝试从 WindowsTM 下的系统 PATH 加载共享库 foo.dll 或从 SolarisTM 操作环境下的 LD_LIBRARY_PATH 加载 libfoo.so。
JVMTI -agentpath 命令行选项规范更改
-agentpath 命令行规范描述将在 JDK 8 中修改为:
如果
filename
参数在去除任何特定于平台的库前缀、路径和文件扩展名后指示名称为 L 的名称,并且VM 中library
有一个名为 L 的本机库,则该函数必须由代理导出。与参数匹配的文件名不必存在于文件系统中。该函数由 VM 调用,如 JVMTI 规范中所述。调用时将被传递给函数。statically linked``Agent_OnLoad_L``Agent_OnLoad_L``options``Agent_OnLoad_L
否则,-agentpath: 后面的路径是加载库的绝对路径。不会发生库名称扩展。将
options
在启动时传递给代理。例如,如果指定选项 -agentpath:/myLibs/foo.so=opt1,opt2,VM 将尝试加载共享库 /myLibs/foo.so。
JVMTI 本机接口规范更改
-
本机 JVMTI 代理可能
statically linked
与 VM 一起使用。库和 VM 映像的组合方式取决于实现。 -
statically linked
当且仅当代理导出一个名为 的函数时,Agent_OnLoad_L
才定义其映像已与 VM 组合的代理 L。 -
如果
statically linked
代理 L 导出一个名为 的函数Agent_OnLoad_L
和一个名为 的函数Agent_OnLoad
,则该Agent_OnLoad
函数将被忽略。 -
如果代理 L 是
statically linked
,Agent_OnLoad_L
则将使用为函数指定的相同参数和预期返回值来调用函数Agent_OnLoad
。 -
代理L将
statically linked
禁止动态加载同名代理。 -
Agent_OnUnload_L
如果导出了代理的函数,VM 将在启动期间的同一点调用动态入口点Agent_OnUnLoad
。 -
如果
statically linked
代理 L 导出一个名为 的函数Agent_OnUnLoad_L
和一个名为 的函数Agent_OnUnLoad
,则该Agent_OnUnLoad
函数将被忽略。 -
如果代理 L 是
statically linked
,Agent_OnAttach_L
则将使用为函数指定的相同参数和预期返回值来调用函数Agent_OnAttach
。 -
如果
statically linked
代理 L 导出一个名为 的函数Agent_OnAttach_L
和一个名为 的函数Agent_OnAttach
,则该Agent_OnAttach
函数将被忽略。
com.sun.tools.attach.VirtualMachine.loadAgentLibrary
该语言将被添加到该方法的 javadoc 中:
如果代理
statically linked
与 VM 一起加载,则Agent_OnAttach
调用的特定函数名称将是特定于库的,如 -agentlib JVMTI 规范部分中定义的那样。
com.sun.tools.attach.VirtualMachine.loadAgentPath
该语言将被添加到该方法的 javadoc 中:
如果代 理
statically linked
与 VM 一起加载,则Agent_OnAttach
调用的特定函数名称将是 -agentpath JVMTI 规范中定义的特定于库的函数名称。
JVMTI 版本规范将增加到 JDK18_JVMTI_VERSION。
JDK18_JVMTI_VERSION 将设置为 0x30010203,即 1.2.3。
支持 JDK18_JVMTI_VERSION 或更高版本的虚拟机将支持此新功能。
影响
- 兼容性:此新功能不应影响现有的动态库。
- 可移植性:静态构建时,JNI 本机源代码需要更改函数名称。
- TCK:JNI 本机库测试需要进行调整,以验证对静态链接本机库的支持。
- TCK:需要调整 JVMTI 代理测试以验证对静态链接代理库的支持。