是什么导致 JNI 调用变慢?
我知道在 Java 中进行 JNI 调用时“跨越边界”很慢。
但是我想知道是什么导致速度变慢? 当进行 JNI 调用时,底层 jvm 实现做了什么,导致速度如此之慢?
I know that 'crossing boundaries' when making a JNI call in Java is slow.
However I want to know what is it that makes it slow?
What does the underlying jvm implementation do when making a JNI call that makes it so slow?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
首先,值得注意的是,我们所说的“慢”是指可能需要数十纳秒的时间。对于简单的本机方法,2010 年,我在 Windows 桌面上测得平均调用时间为 40 ns,在 Mac 桌面上为 11 ns。除非您拨打很多电话,否则您不会注意到。
也就是说,调用本机方法可能比调用普通的 Java 方法慢。原因包括:
一些可能已过时的附加讨论可以在 Steve Wilson 和 Jeff Kesselman 于 2000 年撰写的“Java(tm) Platform Performance: Strategies and Tactics”中的“9.2:检查 JNI 成本”部分中找到。大约是这个的三分之一页面,由@Philip 在下面的评论中提供。
2009 年 IBM DeveloperWorks 论文“使用 Java 本机接口的最佳实践” 提供了一些关于避免 JNI 性能陷阱的建议。
First, it's worth noting that by "slow," we're talking about something that can take tens of nanoseconds. For trivial native methods, in 2010 I measured calls at an average 40 ns on my Windows desktop, and 11 ns on my Mac desktop. Unless you're making many calls, you're not going to notice.
That said, calling a native method can be slower than making a normal Java method call. Causes include:
Some additional discussion, possibly dated, can be found in "Java(tm) Platform Performance: Strategies and Tactics", 2000, by Steve Wilson and Jeff Kesselman, in section "9.2: Examining JNI costs". It's about a third of the way down this page, provided in the comment by @Philip below.
The 2009 IBM developerWorks paper "Best practices for using the Java Native Interface" provides some suggestions on avoiding performance pitfalls with JNI.
值得一提的是,并非所有标有
native
的 Java 方法都是“慢”的。其中一些是内在函数,这使得它们速度极快。要检查哪些是内在的,哪些不是,您可以在 vmSymbols.hpp。It is worth mentioning that not all Java methods marked with
native
are "slow". Some of them are intrinsics that makes them extremely fast. To check which ones are intrinsic and which ones are not, you can look fordo_intrinsic
at vmSymbols.hpp.基本上,JVM 解释性地为每个 JNI 调用构造 C 参数,并且代码没有优化。
中概述了更多详细信息本文
如果您对 JNI 与本机代码的基准测试感兴趣此项目有运行基准测试的代码。
Basically the JVM interpretively constructs the C parameters for each JNI call and the code is not optimized.
There are many more details outlined in this paper
If you are interested in benchmarking JNI vs native code this project has code for running benchmarks.
说到JNI,有两个方向:java调用C++,C++调用java。 Java 通过“native”关键字调用 C++(或 C)非常快,大约 50 个时钟周期。然而,C++调用Java有点慢。我们进行了大量的 Java/C++ 集成,我的经验法则是每次调用 1000 个时钟周期,因此您每秒可以获得大约 200 万次调用。我无法回答您“为什么这么慢”的实际问题,但我大胆猜测,必须做大量工作才能使用可变参数将参数从本机 C++ 堆栈传输到 Java 堆栈,验证任何一致性需要,反之亦然。
但是,还要记住,一旦从 C++ 调用 Java 方法,如果该方法返回复杂的数据结构,则还需要对结果的所有访问进行 JNI 调用。这同样适用于将复杂的 C++ 结构转换为 Java。例如,我们在实践中发现,序列化 C++ std::map的速度要快得多。转换为 JSON,通过 JNI 传递字符串,并让 Java 将其反序列化为 Map(假设您希望将整个映射转换为 Java)。
When talking about JNI, there are two directions: java calling C++, and C++ calling java. Java calling C++ (or C) via the "native" keyword is very fast, around 50 clock cycles. However, C++ calling Java is somewhat slow. We do a great deal of Java/C++ integration, and my rule of thumb is 1000 clock cycles per call, so you can get around 2M calls/second. I cannot answer your actual question of "why is it slow", but I'll hazard a guess that a lot of work has to be done to transfer arguments from the native C++ stack using varargs, onto the Java stack, validate whatever conformance is needed, and vice-versa on the return value.
However, also remember that once you make a call into a Java method from C++, if that method returns a complex data structure, you'll need to make JNI calls for all accesses into the result, as well. The same applies for converting complex C++ structure to Java. We've found in practice for example that it is much faster to serialize a C++ std::map<string,string> to JSON, hand the string across JNI, and have Java deserialize it into a Map<String,String>, assuming you want the entire map converted to Java.