是否可以使用sun.misc.Unsafe在没有JNI的情况下调用C函数?

一段C / C ++代码可以为JNI方法提供一个函数指针数组。 但是有没有办法直接从Java代码内部(不使用JNI或类似代码)调用数组指针指向的函数? JNI以某种方式做了类似的事情,所以必须有办法。 JNI是如何做到的? 是通过sun.misc.Unsafe吗? 即使不是,我们是否可以使用一些不安全的解决方法来获取执行该操作的JVM代码?

我当然不打算在商业上使用它。 我甚至不是专业人士,我只是非常喜欢编码而且我最近一直在研究CUDA,所以我想也许我可以尝试将所有东西混合在一起,但JNI调用的开销会破坏GPU加速代码的目的。

JNI慢吗?

JNI已经进行了很多优化,你应该先尝试一下。 但它确实有一定的开销, 详见细节 。

如果本机函数很简单并且经常调用,则此开销可能很大。 JDK有一个名为Critical Natives的私有API,可以减少调用不需要太多JNIfunction的函数的开销。

重要的原住民

本机方法必须满足以下条件才能成为关键本机:

  • 必须是静态的不是同步的 ;
  • 参数类型必须是原始原始数组 ;
  • 实现不能调用JNI函数,即它不能分配Java对象或抛出exception;
  • 不应该运行很长时间,因为它在运行时阻止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中支持关键本机。此外,仅从已编译的代码调用“关键”版本。 因此,您需要关键和标准实现才能使其正常工作。

此function专为JDK内部使用而设计。 没有公共规范或其他东西。 您可能找到的唯一文档可能是对JDK-7013347的评论。

基准

此基准测试显示,当本机工作负载非常小时,关键本机可比常规JNI方法快几倍。 方法越长,相对开销越小。

JNI呼叫的性能


PS JDK正在进行一项工作,以实现Native MethodHandles,它将作为JNI的更快替代品。 但是,它不太可能出现在JDK 10之前。

  1. http://cr.openjdk.java.net/~jrose/panama/native-call-primitive.html
  2. http://mail.openjdk.java.net/pipermail/panama-dev/2015-December/000225.html