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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1556421/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 14:05:15  来源:igfitidea点击:

Use JNI instead of JNA to call native code?

javajava-native-interfacenativejna

提问by Marcus Leon

JNA seems a fair bit easier to use to call native code compared to JNI. In what cases would you use JNI over JNA?

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

采纳答案by Denis Tulskiy

  1. JNA does not support mapping of c++ classes, so if you're using c++ library you will need a jni wrapper
  2. If you need a lot of memory copying. For example, you call one method which returns you a large byte buffer, you change something in it, then you need to call another method which uses this byte buffer. This would require you to copy this buffer from c to java, then copy it back from java to c. In this case jni will win in performance because you can keep and modify this buffer in c, without copying.
  1. JNA 不支持 C++ 类的映射,因此如果您使用的是 C++ 库,您将需要一个 jni 包装器
  2. 如果你需要大量的内存复制。例如,您调用一个返回大字节缓冲区的方法,更改其中的某些内容,然后您需要调用另一个使用此字节缓冲区的方法。这将要求您将此缓冲区从 c 复制到 java,然后将其从 java 复制回 c。在这种情况下,jni 将赢得性能,因为您可以在 c 中保留和修改此缓冲区,而无需复制。

These are the problems I've encountered. Maybe there's more. But in general performance is not that different between jna and jni, so wherever you can use JNA, use it.

这些是我遇到的问题。也许还有更多。但总的来说,jna 和 jni 之间的性能没有太大区别,所以只要你可以使用 JNA,就使用它。

EDIT

编辑

This answer seems to be quite popular. So here are some additions:

这个答案似乎很受欢迎。所以这里有一些补充:

  1. If you need to map C++ or COM, there is a library by Oliver Chafic, creator of JNAerator, called BridJ. It is still a young library, but it has many interesting features:
    • Dynamic C / C++ / COM interop : call C++ methods, create C++ objects (and subclass C++ classes from Java!)
    • Straightforward type mappings with good use of generics (including much nicer model for Pointers)
    • Full JNAerator support
    • works on Windows, Linux, MacOS X, Solaris, Android
  2. As for memory copying, I believe JNA supports direct ByteBuffers, so memory copying can be avoided.
  1. 如果您需要映射 C++ 或 COM,JNAerator 的创建者 Oliver Chafic 有一个名为BridJ 的库。它仍然是一个年轻的图书馆,但它有许多有趣的功能:
    • 动态 C/C++/COM 互操作:调用 C++ 方法,创建 C++ 对象(以及从 Java 子类化 C++ 类!)
    • 很好地使用泛型的直接类型映射(包括更好的指针模型)
    • 完整的 JNAerator 支持
    • 适用于 Windows、Linux、MacOS X、Solaris、Android
  2. 至于内存复制,我相信JNA支持直接ByteBuffers,所以可以避免内存复制。

So, I still believe that wherever possible, it is better to use JNA or BridJ, and revert to jni if performance is critical, because if you need to call native functions frequently, performance hit is noticeable.

所以,我仍然认为,在可能的情况下,最好使用 JNA 或 BridJ,如果性能至关重要,则返回 jni,因为如果您需要频繁调用本机函数,性能损失是显而易见的。

回答by Pascal Thivent

It's not a direct answer and I have no experience with JNA but, when I look at the Projects Using JNAand see names like SVNKit, IntelliJ IDEA, NetBeans IDE, etc, I'm tend to believe it's a pretty decent library.

这不是一个直接的答案,我对 JNA 没有经验,但是,当我查看使用 JNA项目并看到像 SVNKit、IntelliJ IDEA、NetBeans IDE 等名称时,我倾向于相信它是一个相当不错的库。

Actually, I definitely think I would have used JNA instead of JNI when I had to as it indeed looks simpler than JNI (which has a boring development process). Too bad, JNA wasn't released at this time.

实际上,我绝对认为我会在必须的时候使用 JNA 而不是 JNI,因为它确实看起来比 JNI 简单(它的开发过程很无聊)。太糟糕了,此时 JNA 没有发布。

回答by stonemetal

  1. You are writing code a few years ago before there was JNA or are targeting a pre 1.4 JRE.
  2. The code you are working with is not in a DLL\SO.
  3. You are working on code that is incompatible with LGPL.
  1. 几年前,您正在编写 JNA 之前的代码,或者您的目标是 1.4 之前的 JRE。
  2. 您正在使用的代码不在 DLL\SO 中。
  3. 您正在处理与 LGPL 不兼容的代码。

That is only what I can come up with off the top of my head, though I am not a heavy user of either. It also seems like you might avoid JNA if you wanted a better interface than the one they provide but you could code around that in java.

这只是我能想到的,尽管我不是这两者的重度用户。如果您想要一个比 JNA 提供的接口更好的接口,您似乎也可以避免使用 JNA,但您可以在 java 中围绕它进行编码。

回答by jarnbjo

It's difficult to answer such a generic question. I suppose the most obvious difference is that with JNI, the type conversion is implemented on the native side of the Java/native border, while with JNA, the type conversion is implemented in Java. If you already feel quite comfortable with programming in C and have to implement some native code yourself, I would assume that JNI won't seem too complex. If you are a Java programmer and only need to invoke a third party native library, using JNA is probably the easiest path to avoid the perhaps not so obvious problems with JNI.

很难回答这么笼统的问题。我想最明显的区别是,对于 JNI,类型转换是在 Java/native 边界的 native 一侧实现的,而对于 JNA,类型转换是在 Java 中实现的。如果您已经对用 C 编程感到很舒服并且必须自己实现一些本机代码,我会假设 JNI 看起来不会太复杂。如果您是一名 Java 程序员并且只需要调用第三方本机库,那么使用 JNA 可能是避免 JNI 可能不那么明显的问题的最简单途径。

Although I've never benchmarked any differences, I would because of the design, at least suppose that type conversion with JNA in some situations will perform worse than with JNI. For example when passing arrays, JNA will convert these from Java to native at the beginning of each function call and back at the end of the function call. With JNI, you can control yourself when a native "view" of the array is generated, potentially only creating a view of a part of the array, keep the view across several function calls and at the end release the view and decide if you want to keep the changes (potentially requiring to copy the data back) or discard the changes (no copy required). I know you can use a native array across function calls with JNA using the Memory class, but this will also require memory copying, which may be unnecessary with JNI. The difference may not be relevant, but if your original goal is to increase application performance by implementing parts of it in native code, using a worse performing bridge technology seems not to be the most obvious choice.

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

回答by Augusto

Unless I'm missing something, isn't the main difference between JNA vs JNI that with JNA you can't call Java code from native (C) code?

除非我遗漏了一些东西,否则 JNA 与 JNI 之间的主要区别不是使用 JNA 您不能从本机 (C) 代码调用 Java 代码吗?

回答by Ustaman Sangat

By the way, in one of our projects, we kept a very small JNI foot print. We used protocol buffers for representing our domain objects and thus had only one native function to bridge Java and C (then of course that C function would call a bunch of other functions).

顺便说一下,在我们的一个项目中,我们保留了一个非常小的 JNI 足迹。我们使用协议缓冲区来表示我们的域对象,因此只有一个本机函数来桥接 Java 和 C(当然,那个 C 函数会调用一堆其他函数)。

回答by Mustafa Kemal

I investigated JNI and JNA for performance comparison because we needed to decide one of them to call a dll in project and we had a real time constraint. The results have showed that JNI has greater performance than JNA(approximately 40 times). Maybe there is a trick for better performance in JNA but it is very slow for a simple example.

我调查了 JNI 和 JNA 以进行性能比较,因为我们需要决定其中之一在项目中调用 dll,并且我们有实时限制。结果表明,JNI 的性能比 JNA 高(约 40 倍)。也许在 JNA 中有一个提高性能的技巧,但对于一个简单的例子来说它很慢。

回答by Dawid Kurzyniec

If you want JNI performance but are daunted by its complexity, you may consider using tools that generate JNI bindings automatically. For example, JANET(disclaimer: I wrote it) allows you to mix Java and C++ code in a single source file, and e.g. make calls from C++ to Java using standard Java syntax. For example, here's how you'd print a C string to the Java standard output:

如果您想要 JNI 性能但又被其复杂性吓倒,您可以考虑使用自动生成 JNI 绑定的工具。例如,JANET(免责声明:我编写的)允许您在单个源文件中混合 Java 和 C++ 代码,例如使用标准 Java 语法从 C++ 调用 Java。例如,以下是将 C 字符串打印到 Java 标准输出的方法:

native "C++" void printHello() {
    const char* helloWorld = "Hello, World!";
    `System.out.println(#$(helloWorld));`
}

JANET then translates the backtick-embedded Java into the appropriate JNI calls.

然后 JANET 将反引号嵌入的 Java 转换为适当的 JNI 调用。

回答by user1050755

I actually did some simple benchmarks with JNI and JNA.

我实际上用 JNI 和 JNA 做了一些简单的基准测试。

As others already pointed out, JNA is for convenience. You don't need to compile or write native code when using JNA. JNA's native library loader is also one of the best/easiest to use I've ever seen. Sadly, you can't use it for JNI it seems. (That's why I wrote an alternative for System.loadLibrary()that uses the path convention of JNA and supports seamless loading from the classpath (ie jars).)

正如其他人已经指出的那样,JNA 是为了方便。使用 JNA 时无需编译或编写本机代码。JNA 的本地库加载器也是我见过的最好/最容易使用的加载器之一。可悲的是,您似乎不能将它用于 JNI。(这就是为什么我为 System.loadLibrary()编写了一个替代方案,它使用 JNA 的路径约定并支持从类路径(即 jars)无缝加载。)

The performance of JNA however, can be much worse than that of JNI. I made a very simple test that called a simple native integer increment function "return arg + 1;". Benchmarks done with jmh showed that JNI calls to that function are 15 times faster than JNA.

然而,JNA 的性能可能比 JNI 差很多。我做了一个非常简单的测试,调用了一个简单的原生整数增量函数“return arg + 1;”。使用 jmh 完成的基准测试表明,对该函数的 JNI 调用比 JNA 快 15 倍。

A more "complex" example where the native function sums up an integer array of 4 values still showed that JNI performance is 3 times faster than JNA. The reduced advantage was probably because of how you access arrays in JNI: my example created some stuff and released it again during each summing operation.

一个更“复杂”的示例,其中本机函数对 4 个值的整数数组求和,仍然表明 JNI 性能比 JNA 快 3 倍。减少的优势可能是因为您在 JNI 中访问数组的方式:我的示例创建了一些东西并在每次求和操作期间再次释放它。

Code and test results can be found at github.

代码和测试结果可以在 github上找到。

回答by Walter Oney

In my specific application, JNI proved far easier to use. I needed to read and write continuous streams to and from a serial port -- and nothing else. Rather than try to learn the very involved infrastructure in JNA, I found it much easier to prototype the native interface in Windows with a special-purpose DLL that exported just six functions:

在我的特定应用程序中,事实证明 JNI 更易于使用。我需要从串行端口读取和写入连续流——除此之外别无他物。我没有尝试学习 JNA 中非常复杂的基础结构,而是发现在 Windows 中使用仅导出六个函数的专用 DLL 对本机接口进行原型设计要容易得多:

  1. DllMain (required to interface with Windows)
  2. OnLoad (just does an OutputDebugString so I can know when Java code attaches)
  3. OnUnload (ditto)
  4. Open (opens the port, starts read and write threads)
  5. QueueMessage (queues data for output by the write thread)
  6. GetMessage (waits for and returns data received by the read thread since the last call)
  1. DllMain(需要与 Windows 接口)
  2. OnLoad(只执行 OutputDebugString 以便我知道何时附加 Java 代码)
  3. OnUnload(同上)
  4. Open(打开端口,启动读写线程)
  5. QueueMessage(排队数据以供写入线程输出)
  6. GetMessage(等待并返回自上次调用以来读取线程接收到的数据)