一段C / C ++代码可以为JNI方法提供一个函数指针数组。但是有没有办法直接从Java代码内部(不使用JNI或类似代码)调用数组指针指向的函数? JNI以某种方式做了类似的事情,所以必须有办法。 JNI是如何做到的?是通过sun.misc.Unsafe吗?即使不是,我们是否可以使用一些不安全的解决方法来获取执行此操作的JVM代码?
我当然不打算在商业上使用它。我甚至不是专业人士,我只是非常喜欢编码而且我最近一直在研究CUDA所以我想也许我可以尝试将所有东西混合在一起,但是JNI调用的开销会破坏GPU加速代码的目的。 / p>
答案 0 :(得分:64)
JNI已经进行了很多优化,你应该先尝试一下。但它确实有一定的开销,see details。
如果本机函数很简单并且经常调用,则此开销可能很大。 JDK有一个名为 Critical Natives 的私有API,以减少调用不需要太多JNI功能的函数的开销。
本机方法必须满足以下条件才能成为关键本机:
关键本机的声明看起来像普通的JNI方法,除了
JavaCritical_
代替Java_
; JNIEnv*
和jclass
参数; GetArrayElements
和朋友,你可以立即使用直接数组指针。E.g。 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的评论。
This benchmark表示,当本机工作负载非常小时,关键本机可以比常规JNI方法快几倍。方法越长,相对开销越小。
P.S。 JDK正在进行一项工作,以实现Native MethodHandles,它将作为JNI的更快替代品。 但是它不太可能出现在JDK 10之前。
答案 1 :(得分:1)
这里值得一提的是,another popular opensource JVM具有类似的documented,但不是用于加速某些本机方法的JNI调用的流行方法。
可以使用 @FastNative 和 @CriticalNative 批注对Java本机接口(JNI)进行快速本机调用。这些内置的ART运行时优化可加速JNI转换并替换现已弃用的!bang JNI表示法。注释对非本机方法无效,并且仅可用于bootclasspath上的平台Java语言代码(无Play商店更新)。@FastNative 批注支持非静态方法。如果方法访问Jobject作为参数或返回值,请使用此参数。
@CriticalNative 注释提供了运行本地方法的更快方法,但有以下限制:
- 方法必须是静态的-参数,返回值或隐式this都不能使用对象。
- 仅原始类型被传递给本机方法。
- 本机方法在其函数定义中未使用JNIEnv和jclass参数。
- 该方法必须向RegisterNatives注册,而不是依赖于动态JNI链接。
@FastNative 和 @CriticalNative 批注在执行本机方法时禁用垃圾收集。不要与长时间运行的方法一起使用,包括通常快速但通常不受限制的方法。
暂停到垃圾回收可能会导致死锁。如果尚未在本地释放锁(即在返回托管代码之前),请不要在快速本机调用期间获取锁。这不适用于常规JNI调用,因为ART认为正在执行的本机代码已暂停。
@FastNative 可以将本机方法的性能提高3倍,将 @CriticalNative 的性能提高5倍。
此文档引用了现在已弃用的!bang 表示法,该表示法用于加快Dalvik JVM上的本机调用。