Answers:
这些是我遇到的问题。也许还有更多。但是总体而言,jna和jni之间的性能并没有什么不同,因此无论您在哪里可以使用JNA,都可以使用它。
编辑
这个答案似乎很受欢迎。因此,这里有一些补充:
因此,我仍然相信,只要性能至关重要,最好尽可能使用JNA或BridJ,并恢复为jni,因为如果需要频繁调用本机函数,那么性能会受到明显影响。
JNIEXPORT
函数。我目前正在探索JavaCpp作为使用JNI的选项,但是我不认为香草JNI支持这一点。有没有一种方法可以使用我忽略的原始JNI调用C ++成员函数?
很难回答这样一个笼统的问题。我想最明显的区别是,使用JNI,类型转换是在Java /本地边界的本机端实现的,而使用JNA,类型转换是用Java实现的。如果您已经对使用C进行编程感到很满意,并且必须自己实现一些本机代码,那么我认为JNI看起来不会太复杂。如果您是Java程序员并且只需要调用第三方本机库,则使用JNA可能是避免JNI可能不是那么明显的问题的最简单方法。
尽管我从未对任何差异进行基准测试,但由于设计原因,我至少会假设在某些情况下使用JNA进行类型转换的性能要优于使用JNI进行类型转换。例如,当传递数组时,JNA会在每个函数调用的开始将它们从Java转换为本机,然后在函数调用的结束将它们转换回Java。使用JNI,您可以控制自己何时生成数组的本机“视图”,可能仅创建数组的一部分的视图,将视图保留在多个函数调用中,最后释放视图并决定是否要保留更改(可能需要将数据复制回去)或放弃更改(不需要复制)。我知道您可以使用Memory类通过JNA在函数调用之间使用本机数组,但这也需要复制内存,对于JNI,这可能是不必要的。两者之间的差异可能无关紧要,但是,如果您最初的目标是通过在本机代码中实现部分应用程序来提高应用程序性能,那么使用性能较差的桥接技术似乎并不是最明显的选择。
尽管我都不是其中的重任,但这只是我能想到的。如果您想要一个比其提供的接口更好的接口,似乎也可以避免使用JNA,但是可以在Java中对此进行编码。
顺便说一下,在我们的一个项目中,我们保留了非常小的JNI足迹。我们使用协议缓冲区来表示我们的域对象,因此只有一个本机函数来桥接Java和C(然后,C函数当然会调用其他函数)。
如果要获得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做了一些简单的基准测试。
正如其他人已经指出的那样,JNA是为了方便。使用JNA时,您无需编译或编写本机代码。JNA的本地库加载器也是我见过的最好/最容易使用的加载器之一。可悲的是,您似乎无法将其用于JNI。(这就是为什么我为System.loadLibrary()编写了一个替代方案,该替代方案使用JNA的路径约定并支持从类路径(例如jar)进行无缝加载。)
但是,JNA的性能可能比JNI差很多。我做了一个非常简单的测试,称为简单的本机整数增量函数“ return arg + 1;”。使用jmh进行的基准测试表明,对该函数的JNI调用比JNA快15倍。
一个更“复杂”的示例(本机函数将4个值的整数数组求和)仍然表明,JNI性能比JNA快3倍。降低的优势可能是由于您如何访问JNI中的数组:我的示例创建了一些东西,并在每次求和操作期间再次释放了它们。
代码和测试结果可以在github上找到。
我调查了JNI和JNA以便进行性能比较,因为我们需要确定其中一个在项目中调用dll,并且我们存在实时限制。结果表明,JNI比JNA具有更高的性能(约40倍)。也许有一个技巧可以提高JNA的性能,但是对于一个简单的示例来说却很慢。
除非我有所遗漏,否则JNA与JNI之间的主要区别是否就是使用JNA不能从本地(C)代码调用Java代码?