我正在使用ByteBuffers
和FileChannels
将二进制数据写入文件。对大文件执行此操作或对多个文件执行此操作时,我会收到OutOfMemoryError
异常。
我在其他地方读过,使用Bytebuffers
和NIO会被打破,应该避免。你们中是否有人遇到过这种问题,并找到了一种解决方案,可以在java文件中有效地保存大量的二进制数据?
jvm选项-XX:MaxDirectMemorySize
是否可行?
答案 0 :(得分:6)
我想说不要创建一个包含所有数据的巨大ByteBuffer。创建一个小得多的ByteBuffer,用数据填充它,然后将这些数据写入FileChannel。然后重置ByteBuffer并继续,直到写完所有数据。
答案 1 :(得分:5)
查看Java的 Mapped Byte Buffers ,也称为“直接缓冲区”。基本上,这种机制使用操作系统的虚拟内存分页系统将缓冲区“映射”到磁盘。操作系统将自动,非常快速地管理磁盘和内存中的字节,您不必担心更改虚拟机选项。这也将使您能够利用NIO相对于传统的基于Java流的i / o的改进性能,而不会出现任何奇怪的黑客攻击。
我能想到的唯一两个捕获量是:
Kirk Pepperdine(一位有点着名的Java性能大师)参与了一个网站www.JavaPerformanceTuning.com,该网站还有更多的MBB细节: NIO Performance Tips
答案 2 :(得分:1)
如果你以随机方式访问文件(在这里阅读,跳过,在那里写,然后再回来)那么你就有问题了; - )
但是如果你只写大文件,你应该认真考虑使用流。 java.io.FileOutputStream
可以直接用于逐字节写入文件或包装在任何其他流(即DataOutputStream
,ObjectOutputStream
)中,以方便编写浮点数,整数,字符串甚至可序列化对象。存在类似的类来读取文件。
Streams为您提供了在(几乎)任意小内存中操纵任意大文件的便利。在绝大多数情况下,它们是访问文件系统的首选方式。
答案 3 :(得分:0)
前两个回答似乎很合理。至于命令行开关是否有效,它取决于你的内存使用量达到限制的速度。如果没有足够的ram和虚拟内存可用于至少三倍的可用内存,那么您将需要使用给出的备用建议之一。
答案 4 :(得分:0)
使用transferFrom方法应该对此有所帮助,假设您以增量方式写入通道而不是一次性写入,因为之前的答案也指出了。
答案 5 :(得分:0)
这可能取决于特定的JDK供应商和版本。
某些Sun JVM中的GC存在错误。直接内存的短缺不会在主堆中触发GC,但直接内存由主堆中的垃圾直接ByteBuffers固定。如果主堆大部分是空的,那么很多都不会被收集很长时间。
即使您没有自己使用直接缓冲区,也会烧坏您,因为JVM可能代表您创建直接缓冲区。例如,将非直接的ByteBuffer写入SocketChannel会在封面下创建一个直接缓冲区,用于实际的I / O操作。
解决方法是自己使用少量直接缓冲区,并保留它们以供重复使用。