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

Posted

技术标签:

【中文标题】使用 Bytebuffers 和 NIO 时如何避免 OutOfMemoryError?【英文标题】:How to avoid OutOfMemoryError when using Bytebuffers and NIO? 【发布时间】:2008-08-26 17:23:01 【问题描述】:

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

jvm 选项-XX:MaxDirectMemorySize 是要走的路吗?

【问题讨论】:

【参考方案1】:

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

【讨论】:

【参考方案2】:

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

我能想到的唯一两个问题是:

    在 32 位系统上,所有映射字节缓冲区的总大小限制为略低于 4GB。 (这实际上是我的应用程序的一个限制,我现在在 64 位架构上运行。) 实现是 JVM 特定的,不是必需的。我用的是Sun的JVM,没有问题,但是YMMV。

Kirk Pepperdine(有点著名的 Java 性能大师)参与了一个网站 www.JavaPerformanceTuning.com,该网站有更多 MBB 详细信息:NIO Performance Tips

【讨论】:

感谢您指出所有映射字节缓冲区(仅我的应用程序或所有操作系统上的??)都有一个限制,就我而言,即使我尝试了一个 MappedByteBuffer,我也会得到愚蠢的 OutOfMemoryException对于大约 1.6GB 的文件!但为什么?我应该如何知道我的剩余空间有多大?救命! @Zordid Ummm...MappedByteBuffers(映射是关键!)一般不会导致OutOfMemoryExceptions。别的东西坏了。我建议在 *** 上创建一个新问题……使用代码!有人可能会帮助您。【参考方案3】:

如果您以随机方式访问文件(在此处阅读、跳过、在此处写入、后退),那么您就有问题了 ;-)

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

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

【讨论】:

【参考方案4】:

使用transferFrom 方法应该对此有所帮助,假设您是逐步写入频道而不是像以前的答案所指出的那样一次全部写入。

【讨论】:

【参考方案5】:

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

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

即使您自己不使用直接缓冲区,这也会烧毁您,因为 JVM 可能会代表您创建直接缓冲区。例如,将非直接 ByteBuffer 写入 SocketChannel 会在后台创建一个直接缓冲区以用于实际的 I/O 操作。

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

【讨论】:

【参考方案6】:

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

【讨论】:

以上是关于使用 Bytebuffers 和 NIO 时如何避免 OutOfMemoryError?的主要内容,如果未能解决你的问题,请参考以下文章

由于 Netty 中的 ByteBuffers 导致的内存泄漏

@程序员,React 使用如何避坑?

多线程 ByteBuffers 比顺序慢?

为啥 ByteBuffers hashCodes 是一样的?

Java源代码中的“机械生成”java源文件

ByteBuffers 是在 Android 中存储 > 1MB 数据的好方法吗?