如何最有效地将文件(从Java读取)传递给本机方法?
我有大约30000个文件(每个1MB),我想把它们放进一个本机方法中,它只需要一个字节数组和它的大小作为参数 我浏览了一些示例和基准(如),但它们都做了一些其他有趣的事情 基本上,我不关心文件的内容,我不想访问该文件或字节数组中的某些内容,也不想对其执行任何其他操作。我只想将一个文件放入一个本地方法中,该方法尽可能快地接受字节数组 目前我使用的是RandomAccessFile,但速度非常慢(10MB/s) 有类似的吗如何最有效地将文件(从Java读取)传递给本机方法?,java,file,performance,io,native,Java,File,Performance,Io,Native,我有大约30000个文件(每个1MB),我想把它们放进一个本机方法中,它只需要一个字节数组和它的大小作为参数 我浏览了一些示例和基准(如),但它们都做了一些其他有趣的事情 基本上,我不关心文件的内容,我不想访问该文件或字节数组中的某些内容,也不想对其执行任何其他操作。我只想将一个文件放入一个本地方法中,该方法尽可能快地接受字节数组 目前我使用的是RandomAccessFile,但速度非常慢(10MB/s) 有类似的吗 byte[] readTheWholeFile(File file){ ..
byte[] readTheWholeFile(File file){ ... }
我可以把它放进去
native void fancyCMethod(readTheWholeFile(myFile), myFile.length())
您有什么建议?我不完全确定这是您要问的,但听起来您希望将文件内容作为字节数组有效地传递给本机方法
如果是这种情况,我建议您使用
BufferedInputStream
读取Java中的文件内容,并将其存储在通过分配的ByteBuffer
中,这样就可以将其传递到JNI端并整体访问。现在,在本机方法中,您可以调用直接访问缓冲区。下面是一个readFileFully示例,您可以实现它
public static byte[] readFileFully(String aFileName) throws IOException
{
byte[] retData = null;
File inputFile = new File(aFileName);
if (inputFile == null || !inputFile.exists() || !inputFile.canRead())
{
throw new IOException("INVALID FILE : " + aFileName);
}
// Read in the file data
BufferedInputStream iStream = null;
try
{
iStream = new BufferedInputStream(new FileInputStream(inputFile));
int size = (int)inputFile.length();
retData = new byte[size];
int bytes_read = 0;
// read stuff in here
while (bytes_read < size)
{
bytes_read += iStream.read(retData,bytes_read,size - bytes_read);
}
}
finally
{
if (iStream != null)
{
try
{
iStream.close();
}
catch(IOException e)
{
}
}
inputFile = null;
}
return retData;
}
publicstaticbyte[]readFileFully(stringafilename)抛出IOException
{
字节[]retData=null;
文件输入文件=新文件(文件名);
如果(inputFile==null | | |!inputFile.exists()| | |!inputFile.canRead())
{
抛出新IOException(“无效文件:+aFileName”);
}
//读入文件数据
BufferedInputStream iStream=null;
尝试
{
iStream=new BufferedInputStream(new FileInputStream(inputFile));
int size=(int)inputFile.length();
retData=新字节[大小];
int bytes_read=0;
//在这里读东西
while(字节\读取<大小)
{
bytes_read+=iStream.read(retData,bytes_read,size-bytes_read);
}
}
最后
{
如果(iStream!=null)
{
尝试
{
iStream.close();
}
捕获(IOE异常)
{
}
}
inputFile=null;
}
返回数据;
}
使用常规数组可能效率低下,因为VM在将数组传递给本机代码时可能会复制该数组,并且在I/O期间也可能会使用中间内存
对于最快的IO,使用分配字节缓冲区。底层数组是“特殊的”,因为它不是常规JVM堆的一部分。本机代码和I/O可以直接访问阵列
要将数据读入缓冲区,请使用
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(randomAccessFile.length());
RandomAccessFile.getChannel().read(byteBuffer, 0);
要获取要传递给JNI的备份数组,请使用
byte[] byteArray = byteBuffer.array();
然后可以将该数组和文件长度传递给JNI
直接缓冲区很难创建,因为您的所有文件都是1MB(或大约1MB),您应该能够在多个文件上重用同一缓冲区
希望这有帮助 您不能在本机代码本身中执行i/o有什么原因吗?由于内存模型不同,将数据从Java传递到本机非常昂贵。不,很遗憾,这是不可能的。我希望有一种方法可以让JVM看到,放入本机方法的字节数组不会在Java代码中再次使用,而只是向本机代码提供内存范围,而不是开始复制它…没有办法在Java中获得内存范围感谢您的答案mdma!我只是想知道,我怎样才能确保array()能正常工作?Javadoc说“在调用这个方法之前调用hasArray方法,以确保这个缓冲区有一个可访问的后备数组。”而allocateDirect()告诉我“它是否有后备数组是未指定的。”我想知道这是否会起作用?这是一些更依赖于平台的特性的问题,它依赖于VM。您可以捕获array()抛出的exoption,并使用ByteBuffer.get(byte[])作为回退获取数组。如果您确实直接访问所有虚拟机,那么可以编写一个小型JNI存根方法,该方法接受direct ByteBuffer实例,并调用GetDirectByteBufferAddress,然后将其转发到原始JNI方法。如果ByteBuffer必须将数据复制一次到一个新数组中,那么它会很快-这些都是优化的方法,比将文件逐段读入字节[]要快得多。另外一点可能有助于提高性能-使用多线程。即使您的应用程序将被I/O绑定,I/O也会阻止等待数据(例如,非连续文件)。让多个线程同时读取不同的文件会给您带来加速,特别是使用异步I/O。ForkJoin框架(JSR 166)对于这类工作非常有用,而且非常容易使用:将文件操作重构为一项任务。为要处理的每个文件创建一个任务,并将它们全部放入任务队列中。然后,任务队列以指定的并行级别运行这些任务。