JNI是否很慢?
JNI已经进行了很多优化,你应该先尝试使用它。但是确实存在一定的开销,详见细节。
如果一个本地函数简单并且频繁调用,这种开销可能会很大。JDK有一个私有API叫做Critical Natives,可以减少调用不需要太多JNI功能的函数的开销。
Critical Natives
一个本地方法必须满足以下条件才能成为Critical Native:
- 必须是静态而且非同步的;
- 参数类型必须是基本类型或基本类型数组;
- 实现不能调用JNI函数,即不能分配Java对象或抛出异常;
- 不应该运行太长时间,因为在运行时会阻塞GC。
Critical Native的声明看起来像一个常规的JNI方法,除了
- 它以
JavaCritical_
开头,而不是Java_
;
- 它没有额外的
JNIEnv*
和jclass
参数;
- Java数组通过两个参数传递:第一个是数组长度,第二个是指向原始数组数据的指针。也就是说,不需要调用
GetArrayElements
和相关函数,可以直接使用直接数组指针。
例如,JNI方法
JNIEXPORT jint JNICALL
Java_com_package_MyClass_nativeMethod(JNIEnv* env, jclass klass, jbyteArray array) {
jboolean isCopy;
jint length = (*env)->GetArrayLength(env, array);
jbyte* buf = (*env)->GetByteArrayElements(env, array, &isCopy);
jint result = process(buf, length);
(*env)->ReleaseByteArrayElements(env, array, buf, JNI_ABORT);
return result;
}
将会变成
JNIEXPORT jint JNICALL
JavaCritical_com_package_MyClass_nativeMethod(jint length, jbyte* buf) {
return process(buf, length);
}
Critical natives仅在JDK 7及以上的HotSpot JVM中得到支持。此外,“critical”版本仅从编译代码中调用。因此,为了使其正常工作,您需要同时使用关键和标准实现。
该功能是为JDK内部使用而设计的。没有公共规范或其他东西。可能唯一可以找到的文档是在
JDK-7013347的注释中。
基准测试
这个基准测试显示,当本地负载非常小的时候,关键本地方法可以比常规JNI方法快几倍。方法越长,相对开销就越小。
![Performance of JNI calls](https://istack.dev59.com/cbjA7.webp)
P.S. JDK正在进行一个工作,实现本地MethodHandles,将作为JNI的更快替代品。但是在JDK 10之前出现的可能性不大。
- http://cr.openjdk.java.net/~jrose/panama/native-call-primitive.html
- http://mail.openjdk.java.net/pipermail/panama-dev/2015-December/000225.html