JNI /外部库调用需要直接缓冲区吗?

时间:2016-05-09 07:35:13

标签: java java-native-interface

我想将Java对象(尤其是字节数组)的地址传递给在一次JNI调用中返回的系统调用和外部函数。

OpenJDK自己的JNI函数的答案显然是 no - 例如文件流使用的readBytes / writeBytes io_util总是通过malloc为与外部相关的任何东西引入直接缓冲区/堆调用

但是,为什么?我检查了java.nio.Bits(copyFromArray)和sun.misc.Unsafe(copyMemory)中的代码,并且非常清楚Java原始数组的内容可以直接在普通的C代码中访问没有任何特殊处理(例如通知VM存储器固定或处理非连续块),只要它在当前JNI功能的范围内。因此,当调用JNI方法时,GC似乎不会发生,但如果这是真的,为什么readBytes / writeBytes总是在C堆中复制数据?

有经验的人吗?我没有寻求OpenJDK或Oracle的官方建议/推荐。对可移植性或当前实现之外的任何内容不感兴趣。

1 个答案:

答案 0 :(得分:2)

使用GetPrimitiveArrayBytesCritical()固定数组非常轻量级,完全是出于此目的。但JNI规范明确描述了Get / Release之间可以执行的操作......关键:

  

在调用GetPrimitiveArrayCritical之后,本机代码在调用ReleasePrimitiveArrayCritical之前不应该运行很长一段时间。我们必须将这对函数中的代码视为在关键区域中运行。"在关键区域内,本机代码不能调用其他JNI函数,也不能调用可能导致当前线程阻塞并等待另一个Java线程的任何系统调用。 (例如,当前线程不得对另一个Java线程正在写入的流调用read。)

此外,该文档解释了JVM可能在内部以不同的格式表示数组,然后即使这些函数也会返回一个副本。相反,DirectByteBuffer保证与C兼容格式。

您的简单实验无法解释的副作用是在Java或C中抛出的异常,从其他线程访问,显然 - 替代JVM。规范是在64位虚拟内存寻址变得普遍之前设置的,现在没有强有力的理由来改变它们。

所有JNI都冻结多年,因为它已经足够好了#34;一方面,"不够重要"另一方面,主要关注的是兼容性,鉴于独立实施的数量,这本身就是一个严峻的挑战。