【发布时间】:2019-10-05 19:17:20
【问题描述】:
与 JNI 相比,JNA 似乎更容易用于调用本机代码。在什么情况下你会使用 JNI 而不是 JNA?
【问题讨论】:
-
我们选择 JNA 而不是 JNI 的一个重要因素是,JNA 不需要对我们的原生库进行任何修改。必须自定义原生库才能使用 Java 对我们来说是个大问题。
标签: java java-native-interface native jna
与 JNI 相比,JNA 似乎更容易用于调用本机代码。在什么情况下你会使用 JNI 而不是 JNA?
【问题讨论】:
标签: java java-native-interface native jna
这些是我遇到的问题。也许还有更多。但总的来说 jna 和 jni 的性能差别不大,所以只要你能用 JNA,就用它。
编辑
这个答案似乎很受欢迎。所以这里有一些补充:
所以,我仍然认为,只要有可能,最好使用 JNA 或 BridJ,如果性能至关重要,则恢复为 jni,因为如果您需要频繁调用原生函数,性能损失是显而易见的。
【讨论】:
JNIEXPORT函数。我目前正在探索 JavaCpp 作为一个选项,它使用 JNI,但我认为 vanilla JNI 不支持这一点。有没有办法使用我忽略的 vanilla JNI 调用 C++ 成员函数?
很难回答这样一个笼统的问题。我想最明显的区别是,使用 JNI,类型转换是在 Java/native 边界的 native 侧实现的,而使用 JNA,类型转换是在 Java 中实现的。如果您已经对使用 C 编程感到很自在并且必须自己实现一些本机代码,我会假设 JNI 看起来不会太复杂。如果您是 Java 程序员并且只需要调用第三方原生库,那么使用 JNA 可能是避免 JNI 可能不那么明显的问题的最简单方法。
虽然我从未对任何差异进行基准测试,但由于设计原因,我至少会假设在某些情况下使用 JNA 进行类型转换会比使用 JNI 执行得更差。例如,在传递数组时,JNA 将在每个函数调用开始时将它们从 Java 转换为原生,并在函数调用结束时转换回来。使用 JNI,您可以控制自己何时生成数组的本机“视图”,可能只创建数组的一部分的视图,在多个函数调用中保持视图,最后释放视图并决定是否需要保留更改(可能需要将数据复制回来)或丢弃更改(无需复制)。我知道您可以使用 Memory 类在 JNA 的函数调用中使用本机数组,但这也需要内存复制,这对于 JNI 可能是不必要的。差异可能无关紧要,但如果您最初的目标是通过在本机代码中实现部分应用程序来提高应用程序性能,那么使用性能较差的桥接技术似乎不是最明显的选择。
【讨论】:
这只是我能想到的,尽管我不是任何一个的重度用户。如果您想要一个比他们提供的接口更好的接口,您似乎也可以避免使用 JNA,但您可以在 java 中编写代码。
【讨论】:
顺便说一下,在我们的一个项目中,我们保留了非常小的 JNI 足迹。我们使用协议缓冲区来表示我们的域对象,因此只有一个本地函数来桥接 Java 和 C(当然,那个 C 函数会调用一堆其他函数)。
【讨论】:
这不是一个直接的答案,我没有使用 JNA 的经验,但是当我查看 Projects Using JNA 并看到 SVNKit、IntelliJ IDEA、NetBeans IDE 等名称时,我倾向于认为它是一个相当不错的库.
实际上,我绝对认为我会在必要时使用 JNA 而不是 JNI,因为它确实看起来比 JNI 更简单(开发过程很无聊)。太糟糕了,此时 JNA 还没有发布。
【讨论】:
我实际上用 JNI 和 JNA 做了一些简单的基准测试。
正如其他人已经指出的那样,JNA 是为了方便。使用 JNA 时无需编译或编写本机代码。 JNA 的本机库加载器也是我见过的最好/最容易使用的库之一。可悲的是,您似乎不能将它用于 JNI。 (这就是我写an alternative for System.loadLibrary()的原因,它使用JNA的路径约定并支持从类路径(即jars)无缝加载。)
但是,JNA 的性能可能比 JNI 差很多。我做了一个非常简单的测试,它调用了一个简单的本机整数增量函数“return arg + 1;”。使用 jmh 完成的基准测试表明,对该函数的 JNI 调用比 JNA 快 15 倍。
一个更“复杂”的示例,其中本机函数对一个包含 4 个值的整数数组求和,但仍然表明 JNI 的性能比 JNA 快 3 倍。减少的优势可能是因为您在 JNI 中访问数组的方式:我的示例创建了一些东西并在每次求和操作期间再次释放它。
代码和测试结果可以在at github找到。
【讨论】:
如果您想要 JNI 性能但又被其复杂性吓倒,您可以考虑使用自动生成 JNI 绑定的工具。例如,JANET(免责声明:我写的)允许您在单个源文件中混合 Java 和 C++ 代码,例如使用标准 Java 语法从 C++ 调用 Java。例如,下面是如何将 C 字符串打印到 Java 标准输出:
native "C++" void printHello() {
const char* helloWorld = "Hello, World!";
`System.out.println(#$(helloWorld));`
}
JANET 然后将反引号嵌入的 Java 转换为适当的 JNI 调用。
【讨论】:
我调查了 JNI 和 JNA 以进行性能比较,因为我们需要决定其中一个来调用项目中的 dll,并且我们有一个实时限制。结果表明,JNI 比 JNA 具有更高的性能(大约 40 倍)。也许在 JNA 中有一个提高性能的技巧,但对于一个简单的例子来说它非常慢。
【讨论】:
除非我遗漏了什么,否则 JNA 与 JNI 之间的主要区别不是使用 JNA 您不能从本机 (C) 代码调用 Java 代码吗?
【讨论】:
在我的特定应用程序中,JNI 被证明更容易使用。我需要从串行端口读取和写入连续流——仅此而已。与其尝试学习 JNA 中非常复杂的基础架构,我发现在 Windows 中使用仅导出六个函数的专用 DLL 对本机接口进行原型设计要容易得多:
【讨论】: