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

时间:2016-03-30 01:35:23

标签: java c jvm java-native-interface native-code

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

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

2 个答案:

答案 0 :(得分:64)

JNI慢了吗?

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

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

重要原生

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

  • 必须静态未同步;
  • 参数类型必须是原始原始数组;
  • 实现不能调用JNI函数,即它不能分配Java对象或抛出异常;
  • 不应该运行很长时间,因为会在运行时阻止GC

关键本机的声明看起来像普通的JNI方法,除了

  • JavaCritical_代替Java_;
  • 开头
  • 它没有额外的JNIEnv*jclass参数;
  • Java数组以两个参数传递:第一个是数组长度,第二个是指向原始数组数据的指针。也就是说,不需要调用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方法快几倍。方法越长,相对开销越小。

Performance of JNI calls

P.S。 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

答案 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上的本机调用。