为什么MappedByteBuffer的array()方法不起作用?

时间:2011-12-21 15:27:20

标签: java wolfram-mathematica memory-mapped-files jlink

我是Java的新手,并尝试使用Mathematica的Java接口使用内存映射来访问文件(希望提高性能)。

我所拥有的Mathematica代码(我相信)相当于以下Java代码(基于this):

import java.io.FileInputStream;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

public class MainClass {
  private static final int LENGTH = 8*100;

  public static void main(String[] args) throws Exception {
    MappedByteBuffer buffer = new FileInputStream("test.bin").getChannel().map(FileChannel.MapMode.READ_ONLY, 0, LENGTH);
    buffer.load();
    buffer.isLoaded(); // returns false, why?
  }
}

我想在缓冲区上使用array()方法,所以我首先尝试使用load()将缓冲区内容加载到内存中。但是,即使在load()之后,isLoaded()返回falsebuffer.array()也会引发异常:java.lang.UnsupportedOperationException at java.nio.ByteBuffer.array(ByteBuffer.java:940)

为什么不加载缓冲区以及如何调用array()方法?

我的最终目标是使用double获取asDoubleBuffer().array()的数组。方法getDouble()确实可以正常工作,但我希望能够一次性完成这项工作以获得良好的性能。我做错了什么?


正如我在Mathematica中所做的那样,我将发布我使用过的实际Mathematica代码(相当于Java中的上述代码):

Needs["JLink`"]
LoadJavaClass["java.nio.channels.FileChannel$MapMode"]
buffer = JavaNew["java.io.FileInputStream", "test.bin"]@getChannel[]@map[FileChannel$MapMode`READUONLY, 0, 8*100]

buffer@load[]
buffer@isLoaded[] (* returns False *)

2 个答案:

答案 0 :(得分:4)

据Javadoc说 “映射字节缓冲区的内容可以随时更改,例如,如果该程序或其他程序更改了映射文件的相应区域的内容。是否发生此类更改,以及何时发生,是依赖于操作系统的,因此未指定。

映射字节缓冲区的全部或部分可能随时变得不可访问,例如,如果映射文件被截断。尝试访问映射字节缓冲区的不可访问区域不会更改缓冲区的内容,并且会在访问时或稍后的某个时间引发未指定的异常。因此,强烈建议采取适当的预防措施,以避免此程序或同时运行的程序操纵映射文件,但读取或写入文件的内容除外。“

对我来说,似乎有许多条件和不良行为。你特别需要这门课吗?

如果您只需要以最快的方式阅读文件内容,请尝试:

FileChannel fChannel = new FileInputStream(f).getChannel();
    byte[] barray = new byte[(int) f.length()];
    ByteBuffer bb = ByteBuffer.wrap(barray);
    bb.order(ByteOrder.LITTLE_ENDIAN);
    fChannel.read(bb);

它的工作速度几乎等于磁盘系统测试速度。

对于double,你可以使用DoubleBuffer(如果f.length()/ 4 size则使用double []数组)或者只调用ByteBuffer的getDouble(int)方法。

答案 1 :(得分:0)

Java中的

final byte[] hb;                  // Non-null only for heap buffers

所以它甚至没有为MappedByteBuffer实现,而是用于HeapByteBuffer。

Android中的

**
     * Child class implements this method to realize {@code array()}.
     *
     * @see #array()
     */
    abstract byte[] protectedArray();

并且再次没有在MappedByteBuffer中,但是例如ByteArrayBuffer确实实现了支持数组。

 @Override byte[] protectedArray() {
    if (isReadOnly) {
      throw new ReadOnlyBufferException();
    }
    return backingArray;
  }

内存映射点应该是堆栈。支持数组将在堆上。
如果你可以从RandomAccessFile打开FileChannel,然后在通道上调用map,你也可以使用MappedByteBuffer上的批量get()方法读入byte []。这将从堆中复制,避免IO,再次进入堆。

buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size());
byte[] b = new byte[buffer.limit()];
buffer.get(b);