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

问题描述 投票:21回答:2

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

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

java c jvm java-native-interface native-code
2个回答
64
投票

JNI慢吗?

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

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

重要的原住民

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

  • 必须是静态的而不是同步的;
  • 参数类型必须是原始或原始数组;
  • 实现不能调用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的评论。

基准

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

Performance of JNI calls


附: 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
投票

值得一提的是,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表示法。

© www.soinside.com 2019 - 2024. All rights reserved.