使用JNI而不是JNA来调用本机代码?

问题

与JNI相比,JNA似乎更容易调用本机代码。在什么情况下你会使用JNI而不是JNA?


#1 热门回答(120 赞)

  • JNA不支持c类的映射,所以如果你使用c库,你需要一个jni包装器
  • 如果你需要大量的内存复制。例如,你调用一个返回大字节缓冲区的方法,更改其中的某些内容,然后需要调用另一个使用此字节缓冲区的方法。这将要求你将此缓冲区从c复制到java,然后将其从java复制回c。在这种情况下,jni将在性能上获胜,因为你可以在c中保留和修改此缓冲区,而无需复制。

这些是我遇到的问题。也许还有更多。但总的来说,jna和jni之间的性能并没有那么不同,所以无论你在哪里使用JNA,都要使用它。
编辑
这个答案似乎很受欢迎。所以这里有一些补充:

  • 如果你需要映射C或COM,那么JNAerator的创建者Oliver Chafic就有一个名为BridJ的库。它仍然是一个年轻的库,但它有许多有趣的特性:动态C / C / COM互操作:调用C方法,创建C对象(以及Java中的子类C类!)直接类型映射,充分利用泛型(包括更好的指针模型)完整的JNAerator支持适用于Windows,Linux,MacOS X,Solaris,Android
  • 至于内存复制,我相信JNA支持直接ByteBuffers,因此可以避免内存复制。

因此,我仍然相信,只要有可能,最好使用JNA或BridJ,如果性能至关重要,则恢复为jni,因为如果需要频繁调用本机函数,性能损失是显而易见的。


#2 热门回答(28 赞)

回答这样一个普遍的问题很难。我认为最明显的区别是,使用JNI,类型转换是在Java /本机边界的本机端实现的,而使用JNA时,类型转换是用Java实现的。如果你已经对使用C语言编程感到很舒服并且必须自己实现一些本机代码,我会认为JNI似乎不会太复杂。如果你是Java程序员并且只需要调用第三方本机库,那么使用JNA可能是避免JNI可能不那么明显问题的最简单方法。

虽然我从来没有对任何差异进行基准测试,但我会因为设计,至少假设在某些情况下使用JNA的类型转换将比使用JNI更糟糕。例如,当传递数组时,JNA会在每个函数调用开始时将这些从Java转换为本机,并在函数调用结束时返回。使用JNI,你可以在生成数组的本机"视图"时控制自己,可能只创建数组的一部分视图,保持多个函数调用的视图,最后释放视图并决定是否需要保留更改(可能需要复制数据)或丢弃更改(不需要复制)。我知道你可以使用Memory类在JNA函数调用中使用本机数组,但这也需要内存复制,这对于JNI来说可能是不必要的。差异可能不相关,但如果你的最初目标是通过在本机代码中实现部分应用程序性能来提高应用程序性能,则使用性能较差的桥接技术似乎不是最明显的选择。


#3 热门回答(8 赞)

  • 你在几年前编写代码之前有JNA或者目标是1.4之前的JRE。
  • 你正在使用的代码不在DLL \ SO中。
  • 你正在处理与LGPL不兼容的代码。

这只是我能想到的,尽管我不是一个沉重的用户。如果你想要一个比他们提供的接口更好的接口,你似乎可以避免使用JNA,但你可以在java中编写代码。