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

Java 使用Bytebuffers和NIO时如何避免OutOfMemoryError?,java,nio,bytebuffer,filechannel,Java,Nio,Bytebuffer,Filechannel,我正在使用ByteBuffers和FileChannels将二进制数据写入文件。当对大文件或多个文件执行此操作时,我得到一个OutOfMemoryError异常。 我在其他地方读到过,在NIO中使用Bytebuffers是错误的,应该避免。你们中是否有人已经遇到过这种问题,并找到了一种解决方案,可以在java中高效地将大量二进制数据保存到文件中 jvm选项-XX:MaxDirectMemorySize是否可行?我想说的是,不要创建一个同时包含所有数据的大型ByteBuffer。创建一个小得多的B

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


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

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

如果以随机方式访问文件(在此处读取、跳过、在此处写入、向后移动),则会出现问题;-)

<>但是如果你只写大文件,你应该<强>严重<强>考虑使用流。code>java.io.FileOutputStream可直接用于逐字节写入文件,或封装在任何其他流(即
DataOutputStream
ObjectOutputStream
)中,以方便写入浮点、整数、字符串甚至可序列化对象。类似的类用于读取文件

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

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

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

  • 在32位系统上,所有映射字节缓冲区的总容量限制在4GB以下。(这实际上是我的应用程序的一个限制,我现在运行在64位体系结构上。)
  • 实现是特定于JVM的,不是必需的。我使用Sun的JVM,没有问题,但是YMMV

  • Kirk Pepperdine(一位颇有名气的Java性能大师)参与了一个名为www.JavaPerformanceTuning.com的网站,该网站有更多MBB详细信息:前两个回答似乎相当合理。至于命令行开关能否工作,这取决于内存使用达到极限的速度。如果您没有足够的ram和虚拟内存使可用内存至少增加三倍,则需要使用给出的备选建议之一。

    使用此方法应有助于解决此问题,假设您以增量方式写入通道,而不是像前面的答案所指出的那样一次写入所有内容。

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

    在一些Sun JVM中,GC中存在一个bug。直接内存不足不会触发主堆中的GC,但直接内存被主堆中的垃圾直接字节缓冲区固定。如果主堆大部分是空的,那么它们很长一段时间都不会被收集

    即使您自己不使用直接缓冲区,这也会让您感到痛苦,因为JVM可能会代表您创建直接缓冲区。例如,将非直接ByteBuffer写入SocketChannel会在封盖下创建一个直接缓冲区,用于实际的I/O操作


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

    感谢您指出所有映射字节缓冲区(仅适用于我的应用程序或操作系统上的所有缓冲区)都有限制。在我的情况下,即使我尝试一个映射字节缓冲区来处理大约1.6GB的文件,我也会遇到愚蠢的OutOfMemoryException!但是为什么呢?我怎么才能知道我的剩余空间有多大??救命啊@佐尔迪乌姆
    MappedByteBuffer
    s(Mapped是关键!)通常不会导致
    OutOfMemoryException
    s。别的东西坏了。我建议在这里用代码创建一个关于StackOverflow的新问题!有人可能会帮助你。