Java中的文件流

我目前正在使用JOGL (Java OpenGL绑定)开发3D图形应用程序。 简而言之,我有一个巨大的横向二进制文件。 由于它的大小,我必须在运行时流式传输地形块。 因此,我们明确地看到随机访问问题。 我已经完成了第一个(和脏:))实现(也许它是multithreading的),我正在使用一种愚蠢的方法……这是它的初始化:

dataInputStream = new DataInputStream(new BufferedInputStream(fileInputStream,4 * 1024); dataInputStream.mark(dataInputStream.available()); 

当我需要读取(流)特殊块(我已经知道它在文件中的“偏移量”)时,我正在执行以下操作(对我感到羞耻:)):

 dataInputStream.reset(); dataInputStream.skipBytes(offset); dataInputStream.read(whatever I need...); 

因为我没有经验,这是我能想到的第一件事:)所以,直到现在我已经阅读了3篇有用且非常有趣的文章(我建议你阅读它们,或许如果你对这个主题感兴趣的话)

  1. 字节缓冲区和非堆内存 – 格雷戈里先生似乎在Java NIO中识字。

  2. Java技巧:如何快速阅读文件 [http://nadeausoftware.com/articles/2008/02/java_tip_how_read_files_quickly] – 这是一个有趣的基准。

  3. 文章:调优Java I / O性能 [http://java.sun.com/developer/technicalArticles/Programming/PerfTuning/] – 简单的Sun建议,但请向下滚动并查看那里的“随机访问”部分; 它们显示了RandomAccessFile(RAF)的简单实现,具有自缓冲改进。

格雷戈里先生在他的文章末尾提供了几个* .java文件。 其中之一是FileChannel + ByteBuffer + Mapping(FBM)和RAF之间的基准测试。 他说,与RAF相比,他在使用FBM时注意到了4倍的加速。 我在以下条件下运行此基准:

  1. 偏移量(例如访问位置)是随机生成的(在文件范围内,例如0 – file.length());
  2. 文件大小为220MB;
  3. 1 000 000次访问(75%读取和25%写入)

结果令人震惊:

英国皇家空军约28秒! FBM约0.2秒!

但是,他在这个基准测试中实现RAF并没有自我缓冲(第3篇文章讲述了一个),所以我猜这是“RandomAccessFile.seek”方法调用,谁会如此努力地降低性能。

好的,现在在我学到的所有这些事情之后,有1个问题和1个进退两难的问题:)

问题 :当我们使用“FileChannel.map”映射文件时,Java是否将整个文件内容复制到MappedByteBuffer中? 或者它只是模仿它? 如果它复制,那么使用FBM方法不适合我的情况,是吗?

困境 :取决于你对这个问题的答案……

  1. 如果映射复制文件,那么我似乎只有两种可能的解决方案: RAF +自缓冲 (第3篇文章中的那个)或者使用FileChannel中的位置 (不使用映射)…哪一个会会更好?

  2. 如果映射不复制文件,那么我有3个选项:前两个和FBM本身

编辑 :这是另外一个问题。 你们有些人说,映射不会将文件复制到MappedByteBuffer中。 那么,为什么我不能映射1GB文件呢,我收到“无法映射”的消息……

PS我想通过建议获得满意的答案,因为我无法在互联网上找到关于此主题的一致信息。

谢谢 :)

不,数据没有缓冲。 MappedByteBuffer使用指针引用数据。 换句话说,数据不会被复制,它只是映射到物理内存中。 如果您还没有,请参阅API文档 。

内存映射文件是虚拟内存的一段,它已被分配了与文件或文件类资源的某些部分直接逐字节相关。 此资源通常是物理存在于磁盘上的文件,但也可以是设备,共享内存对象或操作系统可通过文件描述符引用的其他资源。 一旦存在,文件和存储空间之间的这种相关性允许应用程序将映射部分视为主存储器。

资料来源: 维基百科

如果您要经常阅读数据,最好至少缓存一些数据。

对于一个220 MB的文件,我会将整个内存映射到虚拟内存中。 FBM如此之快的原因是它实际上并没有将数据读入内存,只是让它可用。

注意:当您运行测试时,您需要进行比较,例如,当文件在操作系统缓存中时,无论您如何操作,它都会快得多。 您需要多次重复测试才能获得可重现的结果。

您是否注意到如果您运行程序,然后关闭它,然后再次运行它第二次启动速度要快得多? 发生这种情况是因为操作系统已缓存在第一次运行中访问的文件部分,并且不需要为它们访问磁盘。 内存映射文件本质上允许程序访问这些缓冲区,从而最大限度地减少读取时的副本。 请注意,映射文件的内存不会导致它被整体读入内存; 您读取的位和部分是按需从磁盘读取的。 如果操作系统确定内存不足,则可能决定从内存中释放映射文件的某些部分,并将其保留在磁盘上。

编辑:你想要的是FileInputStream.getChannel()。map(),然后将其改编为InputStream,然后将其连接到DataInputStream。