What makes JNI calls slow?
我知道在Java中使用JNI调用时,跨越边界是很慢的。
但是我想知道是什么让它变慢了?当进行JNI调用时,底层的JVM实现会做什么,这会使它变得如此缓慢?
首先,值得注意的是,通过"慢",我们谈论的是可能需要几十纳秒的东西。对于普通的本地方法,2010年我在Windows桌面上的平均调用次数为40次,在Mac桌面上的平均调用次数为11次。除非你打了很多电话,否则你不会注意到的。
也就是说,调用一个本地方法可能比创建一个普通的Java方法调用慢。原因包括:
- JVM不会内联本机方法。它们也不会及时为这个特定的机器编译——它们已经编译了。
- 可以在本地代码中复制Java数组以供访问,并随后复制回。成本可以与阵列的大小成线性关系。我在我的Windows桌面上测量了一个100000阵列的JNI复制平均约75微秒,在Mac上测量了82微秒。幸运的是,可以通过getPrimitiveArrayCritical或newDirectByteBuffer获得直接访问。
- 如果方法被传递给一个对象,或者需要进行回调,那么本机方法很可能会对JVM进行自己的调用。从本机代码访问Java字段、方法和类型需要类似于反射的一些东西。签名在字符串中指定并从JVM中查询。这既慢又容易出错。
- Java字符串是对象,具有长度并进行编码。访问或创建字符串可能需要O(N)副本。
一些附加的讨论,可能过时,可以在"Java"中找到?平台性能:战略和战术",2000年,史蒂夫·威尔逊和杰夫·凯塞尔曼,在"9.2:检查JNI成本"一节中。下面@philip的评论中提供了这一点,大概是这一页的三分之一。
2009个IBM开发人员论文"使用Java本地接口的最佳实践"为避免JNI的性能缺陷提供了一些建议。
值得一提的是,并不是所有用EDCOX1(0)表示的Java方法都是"慢"的。它们中的一些是使它们极快的内在因素。要检查哪些是固有的,哪些不是,可以在vmsymbols.hpp上查找
基本上,JVM解释性地为每个JNI调用构造C参数,代码没有优化。
本文概述了更多的细节。
如果您对基准JNI和本机代码感兴趣,这个项目有运行基准的代码。