2

这看起来并不简单,特别是对于读/写缓冲的 FileChannel。是否有任何开源实现了我可以基于我的实现的地方?


不懂的人说清楚:

FileChannel 在操作系统级别进行缓冲,我想在 Java 级别进行缓冲。阅读此处了解:FileChannel#force 和缓冲


@Peter我想从快速消息流中将一个大文件写入磁盘。缓冲和批处理是要走的路。所以我想在Java中批处理,然后调用FileChannel.write。

4

3 回答 3

5

我建议使用BufferedOutputStream包装 a FileOutputStream。我不相信你会通过使用and看到任何性能改进,如果你走这条路,你会留下很多难以维护的代码。ByteBufferFileChannel

推理非常简单:无论您采用哪种方法,所涉及的步骤都是相同的:

  1. 生成字节。你没有说你打算如何做到这一点,它可能会在等式中引入额外的临时缓冲级别。但无论如何,Java 数据必须转换为字节。
  2. 将字节累积到缓冲区中。您希望在写入数据之前对其进行缓冲,这样您就不会进行大量的小写入。这是给定的。但是缓冲区的位置并不重要。
  3. 跨 JNI 屏障将字节从 Java 堆移动到 C 堆。写入文件是本机操作,它不会直接从 Java 堆中读取。因此,无论您是在 Java 堆上缓冲然后移动缓冲的字节,还是直接缓冲ByteBuffer(是的,您想要一个直接缓冲区),您仍在移动字节。您将使用 进行更多的 JNI 调用ByteBuffer,但这是边际成本。
  4. Invoke fwrite,一个内核调用,将字节从 C 堆复制到内核维护的磁盘缓冲区中。
  5. 将内核缓冲区写入磁盘。这将超过所有其他步骤的总和,因为磁盘很慢

根据您实施这些步骤的具体方式,可能会增加或减少几微秒,但您无法更改基本步骤。

FileChannel确实为您提供了调用选项,force()以确保步骤 #5 实际发生。这实际上可能会降低您的整体性能,因为在写入字节之前底层fsync调用不会返回。如果你真的想这样做,你总是可以从底层流中获取通道。

底线:我敢打赌,您实际上是受 IO 限制的,并且无法治愈节省更好的硬件。

于 2012-09-25T18:19:14.130 回答
4

FileChannel 仅适用于 ByteBuffers,因此它是自然缓冲的。如果您需要额外的缓冲才能将数据从 ByteBuffer 复制到 ByteBuffer 但我不确定您为什么要这样做。

FileChannel 在操作系统级别进行缓冲

FileChannel 确实告诉操作系统该做什么。操作系统通常有一个磁盘缓存,但 FileChannel 不知道是否是这种情况。

我想在 Java 级别做缓冲

你很幸运,因为你别无选择。;) 这是唯一的选择。

于 2012-09-25T16:31:27.210 回答
-2

我将有两个线程,生产者线程产生 ByteBuffers 并将它们附加到队列的尾部,消费者线程每次从队列的头部删除一些 ByteBuffers,并调用 fileChannel.write(ByteBuffer[])。

于 2012-09-25T16:56:48.977 回答