使用Bytebuffers和NIO时如何避免OutOfMemoryError?

时间:2008-08-26 17:23:01

标签: java nio bytebuffer filechannel

我正在使用ByteBuffersFileChannels将二进制数据写入文件。对大文件执行此操作或对多个文件执行此操作时,我会收到OutOfMemoryError异常。 我在其他地方读过,使用Bytebuffers和NIO会被打破,应该避免。你们中是否有人遇到过这种问题,并找到了一种解决方案,可以在java文件中有效地保存大量的二进制数据?

jvm选项-XX:MaxDirectMemorySize是否可行?

6 个答案:

答案 0 :(得分:6)

我想说不要创建一个包含所有数据的巨大ByteBuffer。创建一个小得多的ByteBuffer,用数据填充它,然后将这些数据写入FileChannel。然后重置ByteBuffer并继续,直到写完所有数据。

答案 1 :(得分:5)

查看Java的 Mapped Byte Buffers ,也称为“直接缓冲区”。基本上,这种机制使用操作系统的虚拟内存分页系统将缓冲区“映射”到磁盘。操作系统将自动,非常快速地管理磁盘和内存中的字节,您不必担心更改虚拟机选项。这也将使您能够利用NIO相对于传统的基于Java流的i / o的改进性能,而不会出现任何奇怪的黑客攻击。

我能想到的唯一两个捕获量是:

  1. 在32位系统上,对于所有映射的字节缓冲区,总数限制在4GB 以下。 (这实际上是我的应用程序的限制,现在我在64位体系结构上运行。)
  2. 实现是特定于JVM的,而不是必需的。我使用Sun的JVM并没有问题,但是YMMV。
  3. Kirk Pepperdine(一位有点着名的Java性能大师)参与了一个网站www.JavaPerformanceTuning.com,该网站还有更多的MBB细节: NIO Performance Tips

答案 2 :(得分:1)

如果你以随机方式访问文件(在这里阅读,跳过,在那里写,然后再回来)那么你就有问题了; - )

但是如果你只写大文件,你应该认真考虑使用流。 java.io.FileOutputStream可以直接用于逐字节写入文件或包装在任何其他流(即DataOutputStreamObjectOutputStream)中,以方便编写浮点数,整数,字符串甚至可序列化对象。存在类似的类来读取文件。

Streams为您提供了在(几乎)任意小内存中操纵任意大文件的便利。在绝大多数情况下,它们是访问文件系统的首选方式。

答案 3 :(得分:0)

前两个回答似乎很合理。至于命令行开关是否有效,它取决于你的内存使用量达到限制的速度。如果没有足够的ram和虚拟内存可用于至少三倍的可用内存,那么您将需要使用给出的备用建议之一。

答案 4 :(得分:0)

使用transferFrom方法应该对此有所帮助,假设您以增量方式写入通道而不是一次性写入,因为之前的答案也指出了。

答案 5 :(得分:0)

这可能取决于特定的JDK供应商和版本。

某些Sun JVM中的GC存在错误。直接内存的短缺不会在主堆中触发GC,但直接内存由主堆中的垃圾直接ByteBuffers固定。如果主堆大部分是空的,那么很多都不会被收集很长时间。

即使您没有自己使用直接缓冲区,也会烧坏您,因为JVM可能代表您创建直接缓冲区。例如,将非直接的ByteBuffer写入SocketChannel会在封面下创建一个直接缓冲区,用于实际的I / O操作。

解决方法是自己使用少量直接缓冲区,并保留它们以供重复使用。