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

发布于 2024-08-07 10:28:51 字数 57 浏览 4 评论 0原文

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

素食主义者 2024-08-14 10:28:52

我实际上用 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。

I actually did some simple benchmarks with JNI and 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).)

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.

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.

Code and test results can be found at github.

请叫√我孤独 2024-08-14 10:28:52

如果您想要 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 调用。

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:

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.

清醇 2024-08-14 10:28:52

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

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.

寂寞陪衬 2024-08-14 10:28:52

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

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?

○闲身 2024-08-14 10:28:52

在我的具体应用程序中,JNI 被证明更容易使用。我需要从串行端口读取和写入连续的流——仅此而已。我没有尝试学习 JNA 中非常复杂的基础设施,而是发现使用专用 DLL 来构建 Windows 中的本机接口原型要容易得多,该 DLL 仅导出六个函数:

  1. DllMain(与 Windows 交互所需)
  2. OnLoad(仅执行 OutputDebugString )所以我可以知道Java代码何时附加)
  3. OnUnload(同上)
  4. Open(打开端口,启动读取和写入线程)
  5. QueueMessage(将数据排队以供写入线程输出)
  6. GetMessage(等待并返回读取线程接收的数据,因为最后一次通话)

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:

  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)
触ぅ动初心 2024-08-14 10:28:51
  1. JNA 不支持 C++ 类的映射,因此如果您使用 C++ 库,
  2. 如果需要大量内存复制,则需要 jni 包装器。例如,您调用一个方法,该方法返回一个大字节缓冲区,您更改其中的某些内容,然后您需要调用另一个使用该字节缓冲区的方法。这需要您将此缓冲区从 c 复制到 java,然后将其从 java 复制回 c。在这种情况下,jni 将在性能上获胜,因为您可以在 c 中保留和修改此缓冲区,而无需复制。

这些是我遇到的问题。也许还有更多。但一般来说,jna 和 jni 的性能差别不大,所以只要能使用 JNA 的地方,就使用它。

编辑

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

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

所以,我仍然认为,只要有可能,最好使用 JNA 或 BridJ,如果性能至关重要,则恢复到 jni,因为如果需要频繁调用本机函数,性能损失会很明显。

  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.

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.

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.

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.

奈何桥上唱咆哮 2024-08-14 10:28:51

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

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

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.

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.

〗斷ホ乔殘χμё〖 2024-08-14 10:28:51
  1. 您在几年前编写代码,当时还没有 JNA,或者目标是 1.4 版之前的 JRE。
  2. 您正在使用的代码不在 DLL\SO 中。
  3. 您正在编写与 LGPL 不兼容的代码。

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

  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.

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.

乙白 2024-08-14 10:28:51

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

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).

宁愿没拥抱 2024-08-14 10:28:51

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

实际上,我绝对认为当我不得不这样做时我会使用 JNA 而不是 JNI,因为它确实看起来比 JNI 更简单(它有一个无聊的开发过程)。可惜当时JNA还没有发布。

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文