没有JNI可以用sun.misc.Unsafe调用C函数吗?

Is it possible to use sun.misc.Unsafe to call C functions without JNI?

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

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

JNI 有那么慢吗?

JNI已经优化了很多,你先试试看吧。但它确实有一定的开销,see details.

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

重要原住民

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

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

关键本机的声明看起来像常规的 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 支持关键本地程序。此外,"critical" 版本只能从编译代码中调用。因此,您需要关键和标准的实施才能使其正常工作。

此功能专供 JDK 内部使用。没有 public 规范之类的。可能您能找到的唯一文档是在 JDK-7013347.

的评论中

基准

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


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

这里值得一提的是 another popular opensource JVM has a similar, documented,但不是加速 JNI 调用某些本机方法的普及方法。

Faster native calls to the Java Native Interface (JNI) are available using the @FastNative and @CriticalNative annotations. These built-in ART runtime optimizations speed up JNI transitions and replace the now deprecated !bang JNI notation. The annotations have no effect on non-native methods and are only available to platform Java Language code on the bootclasspath (no Play Store updates).

The @FastNative annotation supports non-static methods. Use this if a method accesses a jobject as a parameter or return value.

The @CriticalNative annotation provides an even faster way to run native methods, with the following restrictions:

  • Methods must be static—no objects for parameters, return values, or an implicit this.
  • Only primitive types are passed to the native method.
  • The native method does not use the JNIEnv and jclass parameters in its function definition.
  • The method must be registered with RegisterNatives instead of relying on dynamic JNI linking.

The @FastNative and @CriticalNative annotations disable garbage collection while executing a native method. Do not use with long-running methods, including usually-fast, but generally unbounded, methods.

Pauses to the garbage collection may cause deadlock. Do not acquire locks during a fast native call if the locks haven't been released locally (i.e. before returning to managed code). This does not apply to regular JNI calls since ART considers the executing native code as suspended.

@FastNative can improve native method performance up to 3x, and @CriticalNative up to 5x.

此文档引用了现已弃用的 !bang 表示法,该表示法用于加速 Dalvik JVM 上的某些本机调用。