JNI 有那么慢吗?
JNI 已经优化了很多,你应该先尝试一下。但它确实有一定的开销,see details。
如果本机函数简单且被频繁调用,则此开销可能会很大。 JDK 有一个名为 Critical Natives 的私有 API,以减少调用不需要太多 JNI 功能的函数的开销。
关键原住民
原生方法必须满足以下条件才能成为关键原生:
- 必须是静态且不同步;
- 参数类型必须是primitive或primitive arrays;
- 实现不得调用 JNI 函数,即不能分配 Java 对象或抛出异常;
- 不应长时间运行,因为它在运行时会阻塞 GC。
关键原生的声明看起来像一个常规的 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);
}
仅从 JDK 7 开始的 HotSpot JVM 支持关键本机。此外,“关键”版本仅从编译代码中调用。因此,您需要关键实现和标准实现才能使其正常工作。
此功能专为 JDK 内部使用而设计。没有公共规范或其他东西。您可能会找到的唯一文档是在 JDK-7013347 的 cmets 中。
基准测试
This benchmark 表明,当本机工作负载非常小时,关键本机可以比常规 JNI 方法快几倍。方法越长,相对开销越小。
P.S. JDK 中正在进行的工作是实现 Native 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