是什么让JNI电话慢?
我知道在Java中进行JNI调用时,“跨越边界”的速度很慢。
不过,我想知道它是什么让它变慢? 在进行JNI调用时,底层的jvm实现是如何做的?
首先,值得注意的是,通过“慢”,我们正在谈论可能需要几十纳秒的事情。 对于微不足道的本地方法,在2010年,我在Windows桌面上测量的平均通话时间为40 ns,在我的Mac桌面上通话时间为11 ns。 除非你打了很多电话,否则你不会注意到。
也就是说,调用本地方法可能比创build普通的Java方法调用要慢 。 原因包括:
- 本机方法不会被JVM内联。 它们也不会为这个特定的机器而及时编译 – 它们已经被编译了。
- 一个Java数组可能被复制用本地代码访问,然后被复制回来。 数组的大小可以是线性的。 我测量了10万个arrays的JNI 拷贝 ,在我的Windows桌面上平均约为75微秒,在Mac上平均为82微秒。 幸运的是,直接访问可以通过GetPrimitiveArrayCritical或NewDirectByteBuffer来获得。
- 如果该方法传递了一个对象,或者需要进行callback,那么本地方法可能会自己调用JVM。 从本地代码访问Java字段,方法和types需要类似于reflection的东西。 签名在string中指定,并从JVM中查询。 这既缓慢又容易出错。
- Javastring是对象,长度和编码。 访问或创buildstring可能需要O(n)副本。
另外一些可能过时的讨论可以在Steve Wilson和Jeff Kesselman的“Java:Platform Performance:Strategies and Tactics”2000年的“9.2:检查JNI成本”一节中find。 这是下面的@Philip评论中提供的大约三分之一的内容。
2009年IBM developerWorks论文“使用Java本机接口的最佳实践”提供了一些避免JNI性能缺陷的build议。
基本上,JVM解释性地为每个JNI调用构造C参数,代码没有被优化。
本文列出了更多的细节
如果您对基准JNI与原生代码感兴趣,则此项目具有运行基准testing的代码。
值得一提的是,并不是所有用native
标记的Java方法都是“慢”的。 其中有些是内在的 ,使他们非常快速。 要检查哪些是内在的,哪些不是,可以在do_intrinsic
处查找do_intrinsic 。