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