【问题标题】:File Streaming in JavaJava 中的文件流式传输
【发布时间】:2011-01-18 20:11:03
【问题描述】:

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

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

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

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

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

  1. Byte Buffers and Non-Heap Memory - Gregory 先生似乎精通 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) 的简单实现。

先生。 Gregory 在他的文章末尾提供了几个 *.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. 如果映射复制文件,那么我似乎只有 2 个可能的解决方案:RAF + 自缓冲(第 3 篇文章中的那个)或 ma​​ke使用 FileChannel 中的位置(不使用映射)...哪个更好?

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

编辑:还有一个问题。你们中的一些人说映射不会将文件复制到 MappedByteBuffer 中。好吧,那为什么我不能映射 1GB 文件,我收到“映射失败”消息...

P. S.我想收到一个完整的答案和建议,因为我无法在互联网上找到关于这个主题的一致信息。

谢谢:)

【问题讨论】:

    标签: java streaming random-access


    【解决方案1】:

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

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

    来源:Wikipedia

    如果您要非常频繁地读取数据,最好至少缓存一些数据。

    【讨论】:

    • 如果说 MappedByteBuffer 是指向 HD 的指针,那么它是如何在基准测试中达到如此好的结果的呢?我个人知道的 IO 中唯一可能的加速功能是尽可能少地访问磁盘,这里唯一的解决方案是缓冲。同样,如果您对此问题有足够的了解,请提供更详细的信息。
    • @Haroogan 我从那篇文章中引用:“差异几乎完全是由于内核上下文切换”
    • 你把我介绍给 javadoc 一定是在开玩笑,不是吗?因为,我没有要求提供任何特定信息。我仍然没有任何直接的答案或适当的想法和可能的解决方案。
    • @Haroogan 首先,睁开你的眼睛。考虑到您只想知道“映射是否复制文件”,我的回答就足够了。 javadoc 的第一行说数据是内存映射的。你应该问我这是什么意思,而不是说我的回答是个笑话。无论如何,我的其余答案都说明了它是什么。此外,我对如何优化给出了额外的建议。
    • 您似乎只是不明白。我会用一个原始的问题再试一次。只说是或否。如果我映射 1GB 文件,则 MappedByteBuffer 容量 = 1GB,那么 MappedByteBuffer 真的占用 1GB 的 RAM 还是只是模拟它?
    【解决方案2】:

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

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

    【讨论】:

    • “可用”是什么意思?只能有 2 个选项:文件完全复制到 MappedByteBuffer(32 位系统的最大大小为 2GB)或 MappedByteBuffer 仅使用后台缓冲、预测逻辑或其他方式模拟此文件......因为我试图映射 1GB文件但它没有这样做,我必须得出结论,它的映射似乎将整个文件复制到 MappedByteBuffer ......还是我仍然错了?请在您的答案中提供更详细的信息。
    • 映射时,操作系统将文件映射到虚拟内存中。文件的页面(通常为 4KB)在您读/写时被带入内存,并缓慢刷新回磁盘。 (或者当您强制刷新时)您无法在 0.2 秒内将 220 MB 的文件读入内存。除非您使用 32 位 JVM,否则我不确定为什么无法映射 1 GB 文件。
    • 是的,我使用的是 32 位 JVM,因此我不明白为什么 1GB 文件映射失败......有什么想法吗?目前我只对阅读感兴趣,所以我不需要刷新等。你刚才说操作系统正在将 4KB 页面加载到虚拟内存中,但你看这就是我之前所说的,i。 e. MappedByteBuffer 只是使用我无法控制的慢背景缓冲逻辑来模拟这个文件。对吗?
    • 32 位操作系统中的 32 位 JVM 只能使用大约 1.2 到 1.5 GB 的虚拟内存。 64 位操作系统上的 32 位 JVM 可以访问更多。在 Solaris 上,它可以访问 3.5 GB。我见过的最大的 64 位 JVM 可以访问 768 GB。两者都不符合理论限制,但您可以看到 64 位 JVM 是完成这项工作的正确工具。您可以控制以什么顺序访问文件的位置,内存中可以拥有的文件数量受硬件限制,读取文件的速度也受硬件限制。
    • Java 使用底层操作系统映射。它确实说明了它是如何做到这一点的,因为它取决于您使用的操作系统。如果您想知道您的操作系统是如何工作的,您需要阅读您的操作系统的文档。
    【解决方案3】:

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

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

    【讨论】:

      猜你喜欢
      • 2010-09-08
      • 2016-08-09
      • 1970-01-01
      • 2018-06-07
      • 1970-01-01
      • 2010-09-12
      • 1970-01-01
      • 2011-02-03
      相关资源
      最近更新 更多