使用JNI而不是JNA来调用本地代码?
与JNI相比,JNA似乎更容易用来调用本地代码。 你会在哪些情况下使用JNI而不是JNA?
- JNA不支持c ++类的映射,所以如果你使用c ++库,你将需要一个jni包装器
- 如果你需要大量的内存复制。 例如,你调用一个方法返回一个大的字节缓冲区,你改变它的内容,然后你需要调用另一个使用这个字节缓冲区的方法。 这需要你将这个缓冲区从c复制到java,然后把它从java复制回c。 在这种情况下,jni会赢得性能,因为你可以在c中保存和修改这个缓冲区,而不需要复制。
这些是我遇到的问题。 也许还有更多。 但总的来说,jna和jni之间的performance并没有什么不同,所以无论你在哪里使用JNA,都可以使用它。
编辑
这个答案似乎很stream行。 所以这里有一些补充:
- 如果您需要映射C ++或COM,则JNAerator的创build者Oliver Chafic将会有一个名为BridJ的库 。 它还是一个年轻的图书馆,但它有许多有趣的特点:
- dynamicC / C ++ / COM互操作:调用C ++方法,创buildC ++对象(以及Java的子类C ++类)
- 使用generics的直接types映射(包括针对指针的更好的模型)
- 完整的JNAerator支持
- 适用于Windows,Linux,MacOS X,Solaris,Android
- 至于内存复制,我相信JNA支持直接的ByteBuffers,所以可以避免内存复制。
所以,我仍然认为,只要有可能,最好使用JNA或者BridJ,如果性能很关键的话,还是回到jni,因为如果你需要经常调用本地函数,性能会受到影响。
回答这样一个通用的问题是很难的。 我想最明显的区别是,在JNI中,types转换是在Java /本地边界的本地端实现的,而在JNA中,types转换是在Java中实现的。 如果你已经对C编程感到很舒服,并且必须自己实现一些本地代码,那么我会认为JNI不会太复杂。 如果您是Java程序员,只需要调用第三方本机库,那么使用JNA可能是避免JNI可能不是那么明显的问题的最简单的途径。
尽pipe我从来没有对任何差异进行过基准testing,但是由于devise原因,至less假设在某些情况下JNA的types转换将比JNI更糟糕。 例如,当传递数组时,JNA会在每个函数调用开始时将这些数据从Java转换为本机,并在函数调用结束时返回。 使用JNI,可以在生成数组的本地“视图”时控制自己,可能只创build数组的一部分的视图,将视图保留在几个函数调用之间,并在最后释放视图并决定是否需要保留更改(可能需要复制数据)或放弃更改(不需要复制)。 我知道你可以通过使用Memory类的JNA跨越函数调用使用本地数组,但是这也需要内存复制,这对于JNI可能是不必要的。 这种差异可能并不相关,但如果您最初的目标是通过在本机代码中实现部分应用程序来提高应用程序性能,那么使用性能较差的桥接技术似乎并不是最明显的select。
- 几年前,您正在编写代码,之前有JNA,或者正在定位1.4 JRE以前的版本。
- 您正在使用的代码不在DLL \ SO中。
- 您正在处理与LGPL不兼容的代码。
这只是我能从头顶上想出来的,虽然我也不是那么重的用户。 如果你想要一个比他们提供的界面更好的界面,你也可以避免使用JNA,但是你可以用java编写代码。
顺便说一下,在我们的一个项目中,我们保留了一个非常小的JNI脚印。 我们使用协议缓冲区来表示我们的域对象,因此只有一个本地函数来衔接Java和C(当然C函数会调用一堆其他函数)。
这不是一个直接的答案,我没有与JNA的经验,但是,当我看着使用JNA的项目 ,看到像SVNKit,IntelliJ IDEA,NetBeans IDE等名称,我倾向于相信这是一个相当不错的图书馆。
实际上,我当然认为我会用JNA而不是JNI,因为它看起来比JNI(它有一个无聊的开发过程)简单得多。 太糟糕了,JNA目前还没有发布。
如果你想要JNI的性能,但是它的复杂性令人头疼,你可以考虑使用自动生成JNI绑定的工具。 例如, JANET (免责声明:我写的)允许您将Java和C ++代码混合到单个源文件中,例如使用标准Java语法从C ++调用Java。 例如,以下是如何将Cstring打印到Java标准输出的方式:
native "C++" void printHello() { const char* helloWorld = "Hello, World!"; `System.out.println(#$(helloWorld));` }
然后JANET将反引用embedded式Java转换成适当的JNI调用。
我调查了JNI和JNA的性能比较,因为我们需要决定其中一个在项目中调用dll,并且我们有一个实时的约束。 结果显示JNI比JNA(约40倍)有更大的performance。 也许在JNA中有更好的performance的技巧,但是对于一个简单的例子来说,这是非常缓慢的。
除非我错过了一些东西,那么JNA和JNI之间的主要区别是,你不能从本地(C)代码中调用Java代码吗?
我实际上做了一些简单的与JNI和JNA的基准。
正如其他人已经指出的那样,JNA是为了方便。 使用JNA时,不需要编译或编写本机代码。 JNA的本地库加载器也是我见过的最好/最容易使用的加载器之一。 可悲的是,你不能用它看起来像JNI。 (这就是为什么我写了一个替代System.loadLibrary()使用JNA的path约定,并支持从类path(即jar)无缝加载。)
然而,JNA的performance可能比JNI差很多。 我做了一个非常简单的testing,调用一个简单的本地整数递增函数“return arg + 1;”。 通过jmh进行的基准testing表明,JNI对该function的调用比JNA快15倍。
一个更复杂的例子,其中本地函数总结了一个4值的整数数组仍然表明,JNI性能比JNA快3倍。 减less的优势可能是因为你如何访问JNI中的数组:我的例子创build了一些东西,并在每次求和操作期间再次释放它。
代码和testing结果可以在githubfind。